Preskoči na sadržaj

Skinovi ​

Skin je jedan izgled okvira uređivača: trake sa alatima, dugmadi i grupa u njoj, bubble menija, padajućih menija, dijaloga, okvira oko sadržaja i okvira za izbor slike. Prop skin ga bira:

vue
<Tiptapify skin="pastel" />

Skin se sastoji od dve stvari: tokena (boje i veličine kao CSS promenljive) i oblika (strukturnih varijanti koje token ne može da izrazi). Dva ugrađena skina su default — klasičan izgled, podignuta alatna traka sa podignutim grupama dugmadi — i pastel, miran izgled. Prilagođeni skinovi su ravnopravni: registerSkin() i inline objekti skinova razrešavaju se potpuno istim mehanizmom, a ugrađeni skinovi su registrovani skinovi, a ne posebni slučajevi.

Terminologija: skin naspram Vuetify teme ​

  • skin — korisnički termin za jedan izgled okvira uređivača: tokeni + oblik. Prop je skin, tipovi su TiptapifySkin*, registar je registerSkin(), korena klasa je tiptapify-skin-{name}.
  • tema (theme) — rezervisano za Vuetify svetle/tamne teme. Tiptapify komponente zadržavaju svoje theme propove napajane trenutnim nazivom Vuetify teme; sistem skinova ih nikada ne dodiruje.

Oni se kombinuju: većina boja ugrađenih tokena upućuje na promenljive Vuetify teme (rgb(var(--v-theme-surface)), …), pa skinovi automatski prate svetli/tamni režim. Ćelije fiksno tamne boje u koloni classic — ručica za promenu veličine (rgba(0, 0, 0, .5/.8)), obična ivica slike, senka bubble menija i senke uzvišenja menija/dijaloga — namerne su: klasičan izgled boji neutralan tamni okvir u obe teme, a izmerena osnova ih fiksira. Prilagođeni skin može slobodno upućivati na promenljive teme — pogledajte tabelu tokena.

Koncept ​

Tokeni ​

Token je CSS custom property sa imenom --tf-*. Ključ kataloga je camelCase oblik CSS sufiksa: toolbarBg → --tf-toolbar-bg, dialogTitleSize → --tf-dialog-title-size. Vrednosti su obični stringovi — boje, kompletne skraćenice ivica, senke, razmaci, radijusi.

Svako pravilo skina u biblioteci postoji jednom i čita svoj token sa rezervnom vrednošću jednakom klasičnoj (osnovnoj) vrednosti:

css
.tiptapify-toolbar-group {
  border-radius: var(--tf-group-radius, 4px);
}

Razrešeni skin emituje samo svoja preklapanja kao promenljive; CSS rezervne vrednosti nose osnovu. Promenljive se pojavljuju u korenu uređivača — i, pošto se meniji i dijalozi teleportuju iz njega, na SkinScope omotaču svakog menija i na samoj kartici dijaloga, pa tokeni prate svaku površinu.

Oblik ​

Šest oznaka odlučuje koji se skupovi pravila primenjuju; tokeni ih boje:

oznakavrednostišta prebacuje
toolbarraised · flatVToolbar uzvišenje + bez unutrašnje tanke linije naspram ravne obojene trake
groupsbox · flatodeljci trake sa alatima kao izdignuti VBtnGroup okviri (markup iz 0.3.2: spojena dugmad, tokeni grupe boje okvir) naspram običnih flex div nizova
buttonsvuetify · quietizvorna Vuetify dugmad naspram „ghost“ dugmadi sa neutralnim stanjem pritiska
bubblecard · flatzadržano radi kompatibilnosti: bubble meniji su jedan red dugmadi sa ikonama obojen tokenima u oba oblika (njihova dugmad prate buttons)
dialogclassic · softrazdelnik ispod naslova, izdignuto zatvaranje naspram meke kartice, tekstualno zatvaranje
menuvuetify · softpadajući meniji: Vuetify lista naspram lagane kartice sa razmakom

Razrešeni oblik je kodiran na korenu uređivača kao šest klasa .tiptapify-shape-{flag}-{value} i izložen kao shape: ComputedRef<ResolvedShape> na useTiptapifyConfig() za prilagođene komponente trake sa alatima.

Extends ​

Skin se razrešava nad klasičnom osnovom, zatim njegovim lancem extends od korena, pa njegovim sopstvenim članovima:

classic (base tokens + classic shape) → extends chain → own colors/metrics/flags

Karike koje nedostaju razrešavaju se na akumulirani lanac (sa upozorenjem u razvoju); ciklus se razrešava na classic (sa upozorenjem u razvoju). Registrovani skin se referencira po imenu; inline objekat skina može da extends registrovano ime i nikada ne ulazi u globalni registar — prilagođeni skinovi nikada ne cure između uređivača. Nepoznati ključevi tokena se ignorišu uz upozorenje u razvoju.

Demonstracije uživo sva tri oblika nalaze se na stranici primera skinova.

Katalog tokena ​

Tabele u nastavku su v1 ugovor: ključevi kataloga (TiptapifySkinColors / TiptapifySkinMetrics) su imena koja možete postaviti. Ćelije classic su CSS rezervne vrednosti — vrednost koju osnovna pravila boje kada nijedan skin ne preklapa token.

Traka sa alatima / traka ​

tokenCSS promenljivaclassicpastel
toolbarBg--tf-toolbar-bgrgb(var(--v-theme-surface))rgba(var(--v-theme-on-surface), 0.03)
toolbarBorderBottom--tf-toolbar-border-bottomnone1px solid rgba(var(--v-border-color), var(--v-border-opacity))
toolbarFg--tf-toolbar-fginheritrgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity))

Stavke / grupe / razdelnici ​

tokenCSS promenljivaclassicpastel
itemsPadding--tf-items-padding12px 16px6px 8px
itemsGap--tf-items-gapnormal ¹6px
itemsRowGap--tf-items-row-gap5px4px
groupGap--tf-group-gap0px2px
groupHeight--tf-group-height32pxauto
groupBg--tf-group-bgrgb(var(--v-theme-surface))transparent
groupRadius--tf-group-radius4px0px
groupShadow--tf-group-shadowVuetify elevation 2 ²none
dividerWidth--tf-divider-width0px (spacer: margin 0 4px)1px
dividerHeight--tf-divider-height0px20px
dividerColor--tf-divider-colortransparentrgba(var(--v-border-color), var(--v-border-opacity))

¹ Izmerena classic ćelija je normal — izvorna flex vrednost Vuetify-a, koju nosi CSS rezervna vrednost. Osnovna vrednost kataloga 0px je ekvivalentna eksplicitna dužina za preklapanja skina.

² Ćelije koje glase „Vuetify elevation n“ su opisne: tačan string trostruke senke je izmerena vrednost u pohranjenom baznom fixture-u (src/skins/__fixtures__/skin-baseline.json) i ujedno CSS rezervna vrednost. Za groupShadow to je elevation 2 (0 3px 1px -2px …, 0 2px 2px 0 …, 0 1px 5px 0 …) — pod groups: 'box' odeljak je VBtnGroup (elevation="4"), a pravilo tokena boji okvir ovom trojkom preko sopstvenog uzvišenja grupe: okvir poseduje uzvišenje, spojena dečja dugmad ga ne nose.

Dugmad ​

Ovi tokeni se koriste samo pod buttons: 'quiet' — pogledajte Dostupnost.

tokenCSS promenljivaquiet vrednost (classic: izvorni Vuetify)
buttonFg--tf-button-fgrgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity))
buttonHoverBg--tf-button-hover-bgrgba(var(--v-theme-on-surface), 0.07)
buttonHoverFg--tf-button-hover-fgrgba(var(--v-theme-on-surface), var(--v-high-emphasis-opacity))
buttonActiveBg--tf-button-active-bgrgba(var(--v-theme-on-surface), 0.1)
buttonActiveHoverBg--tf-button-active-hover-bgrgba(var(--v-theme-on-surface), 0.14)
buttonRadius--tf-button-radius6px

Bubble meniji (izbor, prazan red, slika) ​

tokenCSS promenljivaclassicpastel
bubbleBg--tf-bubble-bgrgb(var(--v-theme-surface))isto
bubbleBorder--tf-bubble-bordernone1px solid rgba(var(--v-border-color), var(--v-border-opacity))
bubbleShadow--tf-bubble-shadow0 4px 14px rgba(0, 0, 0, 0.12), 0 1px 3px rgba(0, 0, 0, 0.08)0 6px 20px rgba(0, 0, 0, 0.12)
bubbleRadius--tf-bubble-radius6px10px
bubblePadding--tf-bubble-padding4pxisto
bubbleGap--tf-bubble-gap2pxisto
tokenCSS promenljivaclassicpastel
menuListPadding--tf-menu-list-padding8px 0px4px
menuRadius--tf-menu-radius4px8px
menuBorder--tf-menu-bordernone1px solid rgba(var(--v-border-color), var(--v-border-opacity))
menuShadow--tf-menu-shadowVuetify elevation 8 ²0 4px 16px rgba(0, 0, 0, 0.08)
menuItemRadius--tf-menu-item-radius0px6px
menuItemPadding--tf-menu-item-paddingVuetify ³4px 8px
menuItemMinHeight--tf-menu-item-min-heightVuetify ³34px
menuItemActiveBg--tf-menu-item-active-bgVuetify ³rgba(var(--v-theme-on-surface), 0.06)

³ Okvir liste (radijus, senka, razmak liste) classic menija je danas pravilo skina — omotač tokena SkinScope stoji između sopstvenog dečjeg pravila Vuetify-a i liste, pa biblioteka vraća te piksele kao pravila skina koja čitaju gornje tokene. Pikseli stavki ostaju izvorni Vuetify (fixture meri Vuetify podrazumevane vrednosti): četiri tokena stavki su neaktivna dok je menu: 'vuetify'.

Dijalozi ​

tokenCSS promenljivaclassicpastel
dialogShadow--tf-dialog-shadowVCard elevation ²0 20px 48px rgba(0, 0, 0, 0.16), 0 2px 6px rgba(0, 0, 0, 0.06)
dialogRadius--tf-dialog-radius4px (VCard)12px
dialogHeaderPadding--tf-dialog-header-padding8px 16px16px 12px 12px 20px
dialogContentPadding--tf-dialog-content-padding16px 24px 24px4px 20px
dialogActionsPadding--tf-dialog-actions-padding8px16px 20px 20px
dialogTitleSize--tf-dialog-title-size1.25rem1.125rem
dialogTitleWeight--tf-dialog-title-weight400600
dialogFieldRadius--tf-dialog-field-radiuspodrazumevano polje (4px)8px (outlined)
dialogActionBtnMinHeight--tf-dialog-action-btn-min-heightVuetify (auto)38px
dialogActionBtnRadius--tf-dialog-action-btn-radiusVuetify (4px)8px
dialogGridGap--tf-dialog-grid-gap24px16px

Okvir (omotač ivice uređivača) ​

tokenCSS promenljivaclassicpastel
frameBorder--tf-frame-border1px solid rgba(var(--v-border-color), var(--v-border-opacity)), bez gornje ivicepuna ivica
frameBg--tf-frame-bgtransparentrgb(var(--v-theme-surface))
frameShadow--tf-frame-shadownone0 1px 2px rgba(0, 0, 0, 0.05)

Pod oblikom ravne trake sa alatima okvir boji i deltu po stranama (gornju ivicu i donju ivicu pri 1,6× neprozirnosti ivice). Ta delta pripada obliku, a ne tokenu: nije dostupna preko tokena.

Slike i ručice za promenu veličine ​

tokenCSS promenljivaclassicpastel
imageRadius--tf-image-radius0.125rem4px
imageBg--tf-image-bgtransparentrgba(var(--v-theme-on-surface), 0.04)
imageOutline--tf-image-outline1px solid rgba(0, 0, 0, 0.25) (izabrana)2px solid rgb(var(--v-theme-primary))
imageOutlineHover--tf-image-outline-hovertransparentrgba(var(--v-theme-primary), 0.35)
handleBg--tf-handle-bgrgba(0, 0, 0, 0.5)rgb(var(--v-theme-primary))
handleBgHover--tf-handle-bg-hoverrgba(0, 0, 0, 0.8) (hover)rgb(var(--v-theme-primary))
handleBorder--tf-handle-bordernone2px solid rgb(var(--v-theme-surface))
handleShadow--tf-handle-shadownone0 0 0 1px rgba(0, 0, 0, 0.15)
handleRadius--tf-handle-radius2px50%

Traka sa alatima slike prati oznaku buttons: pod quiet oblikom dobija quiet površinu — njen razmak od 2px/4px čita --tf-bubble-gap / --tf-bubble-padding sa rezervnim vrednostima menija slike; ne postoji poseban token trake sa alatima slike.

Nije u katalogu (teritorija izlaza u nuždi) ​

Neke dimenzije pomenute u pregledu dizajna nemaju token u katalogu u v1: visina trake sa alatima, širina okvira, radijus okvira, širina dugmeta, razmak dugmeta, širina dijaloga. Tabele kataloga iznad su ugovor; za te piksele otvorite izlaz u nuždi i stilizujte stabilne klase direktno.

Dostupnost ​

Ugovor, naveden jednom i poštovan u svakoj grupi iznad:

Token boji površinu samo kada je oblik aktivira; izvorni Vuetify vizuali stilizuju se bojama Vuetify teme.

Po grupi:

  • Traka sa alatima, okvir, okvir slike/ručice — aktivni u svakom obliku; tokeni ih boje, classic ćelije su izvorni izgled na koji se tokeni vraćaju.
  • Okvir grupa (groupBg, groupRadius, groupHeight, groupShadow) — pod groups: 'box' odeljak je VBtnGroup (struktura iz 0.3.2: dugmad se spajaju, okvir nosi uzvišenje); pravila okvira boje ga tokenima grupe, a --tf-group-height drži ugovor od 32px sa !important (Vuetify pravilo gustine se bori protiv njega, kao u 0.3.2). Pod groups: 'flat' odeljci su obični div nizovi (providni, bez senke, radijus 0 je osnova tog oblika).
  • Tanke linije razdelnika (dividerWidth/Height/Color) — vidljiva tanka linija je mogućnost oblika groups: 'flat'; u obliku box razdelnik je razmaknica nulte veličine (margin: 0 4px iz osnovnog pravila).
  • Razmaci/pomeraji stavki — kompaktan niz 6px 8px vezan je za ravnu traku (toolbar: 'flat'), razmaci grupa za ravne odeljke (groups: 'flat'); izdignuta traka zadržava classic metrike.
  • Dugmad (buttonFg, buttonHover*, buttonActive*, buttonRadius) — neaktivni pod buttons: 'vuetify': izvorna Vuetify dugmad boje svoja stanja. Stilizujte ih bojama Vuetify teme (primary, surface, …) — classic oblici ih namerno nasleđuju.
  • Metrike stavki menija (menuItemRadius/Padding/MinHeight/ActiveBg) — neaktivne pod menu: 'vuetify'; Vuetify poseduje te piksele stavki. Okvir liste menija (radijus/senka/razmak) boji skin u oba oblika (pogledajte napomenu o meniju).
  • Meki izgled dijaloga (dialogActionBtn*, dialogFieldRadius, meki razmaci, odsutni razdelnik) — boji se pod dialog: 'soft'; pod dialog: 'classic' dijalog je VCard sa classic razmacima.
  • Bubble okvir — meniji izbora, praznog reda i slike su jedan red dugmadi sa ikonama u oba oblika, obojen bubble tokenima; bubble više ne menja njihov markup.

Preklapanje jednako osnovi kataloga ne emituje se — pod ne-classic oblikom sopstvena osnova oblika boji se preko CSS rezervne vrednosti, pa „vraćanje“ classic vrednosti (npr. postavljanje buttonActiveBg nazad na rgb(var(--v-theme-primary)) pod buttons: 'quiet') tiho ne radi ništa. Da biste nametnuli vrednost classic izgleda pod ne-classic oblikom, postavite preklapanje koje se razlikuje od osnove kataloga — ekvivalentan literal poput rgba(var(--v-theme-primary), 1) boji identično i preživljava preskakanje.

Tokeni namerno nisu povezani u izvorne putanje — to je ono što drži default identičnim piksel po piksel classic izgledu, dok sve prolazi kroz katalog.

Recepti ​

Proširenje pastela ​

Nasledite quiet oblik i prefarbajte nekoliko površina. Registrovani nazivi rade na bilo kom uređivaču:

ts
// main.ts — call once, before any editor mounts
import { registerSkin } from 'tiptapify'

registerSkin('mint', {
  extends: 'pastel',
  colors: {
    toolbarBg: 'rgba(0, 128, 0, 0.05)',   // mint tint over the pastel wash
    handleBg: 'rgb(0, 100, 0)',           // deep green handles
    handleBgHover: 'rgb(0, 128, 0)',
    imageOutline: '2px solid rgb(0, 100, 0)',
    imageOutlineHover: 'rgba(0, 100, 0, 0.35)',
  },
})
vue
<Tiptapify skin="mint" />

Oblik (flat/quiet/soft) dolazi iz pastela; netaknuti tokeni zadržavaju pastel vrednosti — quiet pozadina dugmadi ostaje neutralna, pomera se samo ono što preklopite. Ovo je skin prikazan uživo na stranici primera.

Proširenje default skina ​

Zadržite classic strukturu, podesite boje:

ts
registerSkin('paper', {
  extends: 'default',
  colors: { toolbarBg: 'rgb(var(--v-theme-background))' },
  metrics: { groupRadius: '8px', itemsPadding: '10px 14px' },
})

extends: 'default' je podrazumevani koren, pa se ključ može izostaviti: { toolbar: 'flat', … } takođe polazi od classic.

Potpuno prilagođeni ​

Bez extends, bez registracije: prosledite objekat u prop skin i slobodno postavite oznake oblika i tokene. name stavlja vašu oznaku na korenu klasu (pogledajte ispod); bez njega je tiptapify-skin-inline.

vue
<script setup lang="ts">
import { defineSkin } from 'tiptapify'

const slate = defineSkin({
  name: 'slate',
  toolbar: 'flat',
  groups: 'flat',
  buttons: 'quiet',
  bubble: 'flat',
  dialog: 'soft',
  menu: 'soft',
  colors: { toolbarBg: 'rgb(30, 41, 59)', toolbarFg: 'rgb(226, 232, 240)' },
  metrics: { buttonRadius: '8px' },
})
</script>

<template>
  <Tiptapify :skin="slate" />
</template>

defineSkin je pomoćna identitetska funkcija za tipizirane konstantne definicije (ne ulazi ni u jedan registar). Oznake oblika mogu se postavljati pojedinačno — svaka izostavljena oznaka ostaje classic.

Izlaz u nuždi: korena klasa ​

Svaki koren uređivača nosi tiptapify-skin-{name} (tiptapify-skin-inline za inline objekat bez imena). Biblioteka nikada ne vezuje nijedno pravilo za naziv skina — klasa je vaša:

css
/* Style the frame of one skin, dimensions the catalog does not cover */
.tiptapify-skin-mint .tiptapify-frame {
  max-width: 720px;
  margin-inline: auto;
}

Stabilne kuke za koje možete pisati pravila:

klasagde
tiptapify-skin-{name}koren uređivača
tiptapify-shape-{flag}-{value} ×6koren uređivača — razrešeni oblik
tiptapify-toolbar · tiptapify-toolbar-items · tiptapify-toolbar-group · menu-dividertraka sa alatima i njeni odeljci
tiptapify-quiet-buttonskoreni trake sa alatima i bubble menija pod buttons: 'quiet'
tiptapify-bubble-row · tiptapify-bubble-dividerred svakog bubble menija i tanka linija između njegovih grupa
tiptapify-bubble-menu · floating-menu · tiptapify-image-menuredovi menija izbora, praznog reda i slike
tiptapify-btn--activepritisnuto dugme bubble menija (aktivna oznaka, plutanje slike)
tiptapify-menu · tiptapify-menu--softsadržaj preklopa padajućeg menija, nosilac pravila mekog menija
tiptapify-skin-scopediv opsega tokena unutar teleportovanih menija
tiptapify-dialog · tiptapify-dialog--softkartica dijaloga i njen meki nosilac
tiptapify-frameomotač ivice uređivača

Odbrana od tema potrošača. Sloj skinova je napravljen da preživi admin teme koje dodaju razmake i prefarbavaju unutrašnjost Vuetify-a: pravila se vezuju za klase oblika/nosioca, koriste udvojene klase (.v-list.v-list, .v-btn.v-btn.v-btn) radi specifičnosti i zadržavaju !important samo tamo gde se u slučaju admin teme iz osnovnog commit-a pokazalo da je neophodan (skup odbrane menu.css). Konvencije koje održavaju vaša preklapanja na istom bojnom polju:

  • ograničite svoj CSS na .tiptapify-skin-{name} (ili klasu oblika) — nikada na gole unutrašnje delove Vuetify-a;
  • za dimenzije iz kataloga birajte tokene (--tf-*), a za ostalo klase;
  • uskladite udvajanje pri preklapanju Vuetify komponenti (pojedinačna .v-list-item gubi od pravila skina).

Vizuelni ugovor (napomena za saradnike) ​

Izračunati stilovi ugrađenih skinova fiksirani su baznim fixture-om src/skins/__fixtures__/skin-baseline.json (13 odeljaka × svetlo/tamno): CSS sloj je gotov kada se izmerena matrica poklapa sa njim. Napomene za čitanje fixture-a:

  • Hover/active dimenzije ne mogu se nametnuti sa stranice, pa snimak čita tekst deklaracije prvog pogođenog CSSOM pravila za svaki hover selektor i razrešava ga preko probnog elementa unutar korena skina (polje resolved) — to je ugovor pobedničkog pravila, a ne živi snimak :hover.
  • _meta.selectors.menu beleži tekst selektora pre SkinScope. Fixture je zamrznut (normativne vrednosti), pa je to polje namerno zastarelo; paket snimanja fiksira trenutni selektor.
  • Fixture nosi svoje odobrene izmene u _meta.notes: restauracija VBtnGroup markupa od 2026-10-18 pod groups: 'box' ponovo je izmerila polja dečje dugmadi groupBtn/buttonsActive iz classic skina (spojeni uglovi, okvir poseduje uzvišenje). Pastel polja su netaknuta — namerna vizuelna promena ažurira samo zahvaćene classic ćelije, beleži napomenu i isporučuje se sa linijom u CHANGELOG-u.

Pogledajte i ​

Objavljeno pod MIT licencom.