Preskoči na sadržaj

Skinovi ​

Skin je jedan izgled okvira uređivača: alatne trake, gumba i grupa u njoj, bubble izbornika, padajućih izbornika, dijaloga, okvira oko sadržaja i okvira za odabir slike. Odabire ga prop skin:

vue
<Tiptapify skin="pastel" />

Skin se sastoji od dvije stvari: tokena (boje i veličine kao CSS varijable) i oblika (strukturnih varijanti koje token ne može izraziti). Dva ugrađena skina su default — klasičan izgled, podignuta alatna traka s podignutim grupama gumba — i pastel, smiren izgled. Prilagođeni skinovi ravnopravni su: registerSkin() i inline objekti skinova razrješavaju se potpuno istim mehanizmom, a ugrađeni skinovi su registrirani skinovi, a ne posebni slučajevi.

Terminologija: skin naspram Vuetify teme ​

  • skin — korisnički pojam za jedan izgled okvira uređivača: tokeni + oblik. Prop je skin, tipovi su TiptapifySkin*, registar je registerSkin(), korijenska klasa je tiptapify-skin-{name}.
  • tema — rezervirano za Vuetifyjeve svijetle/tamne teme. Tiptapify komponente svoje propove theme hrane imenom trenutne Vuetify teme; sustav skinova ih nikad ne dira.

Oni se kombiniraju: većina boja ugrađenih tokena upućuje na varijable Vuetify teme (rgb(var(--v-theme-surface)), …), pa skinovi automatski prate svijetli/tamni način. Ćelije s fiksnom tamnom bojom u stupcu classic — ručka za promjenu veličine (rgba(0, 0, 0, .5/.8)), običan obrub slike, sjena bubble izbornika i sjene uzdignutosti izbornika/dijaloga — namjerne su: klasičan izgled u obje teme crta neutralan tamni okvir, a izmjerena osnovica ih fiksira. Prilagođeni skin može slobodno upućivati na varijable teme — vidi tablicu tokena.

Koncept ​

Tokeni ​

Token je CSS prilagođeno svojstvo imena --tf-*. Ključ kataloga je camelCase oblik CSS sufiksa: toolbarBg → --tf-toolbar-bg, dialogTitleSize → --tf-dialog-title-size. Vrijednosti su obični stringovi — boje, potpune skraćene oznake obruba, sjene, razmaci, radijusi.

Svako pravilo skina u biblioteci postoji jednom i čita svoj token s rezervnom vrijednošću jednakom klasičnoj (osnovnoj) vrijednosti:

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

Razriješeni skin emitira kao varijable samo svoja nadjačavanja; CSS rezervne vrijednosti nose osnovicu. Varijable završavaju na korijenu uređivača — i, jer se izbornici i dijalozi teleportiraju izvan njega, na SkinScope omotaču svakog izbornika i na samoj kartici dijaloga, pa tokeni prate svaku površinu.

Oblik ​

Šest zastavica odlučuje koji se skupovi pravila primjenjuju; tokeni ih boje:

zastavicavrijednostišto prebacuje
toolbarraised · flatVToolbar uzdignutost + bez unutarnje tanke crte naspram ravne obojene trake
groupsbox · flatodjeljci alatne trake kao podignuti okviri VBtnGroup (oznake iz 0.3.2: spojeni gumbi, tokeni grupe boje okvir) naspram običnih flex div nizova
buttonsvuetify · quietizvorni Vuetify gumbi naspram ghost gumba s neutralnim stanjem pritisnutosti
bubblecard · flatzadržano radi kompatibilnosti: bubble izbornici su u oba oblika jedan red ikonskih gumba obojen tokenima (njihovi gumbi prate buttons)
dialogclassic · softrazdjelnik ispod naslova, uzdignuto zatvaranje naspram meke kartice, tekstualno zatvaranje
menuvuetify · softpadajući izbornici: Vuetify popis naspram lagane kartice s razmakom

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

Extends ​

Skin se razrješava nad klasičnom osnovicom, zatim njegovim lancem extends od korijena prema dolje, pa njegovim vlastitim članovima:

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

Karike koje nedostaju razrješavaju se u dotad skupljeni lanac (uz razvojno upozorenje); ciklus se razrješava u classic (uz razvojno upozorenje). Registrirani skin referencira se imenom; inline objekt skina može extends registrirano ime i nikad ne ulazi u globalni registar — prilagođeni skinovi nikad ne cure između uređivača. Nepoznati ključevi tokena zanemaruju se uz razvojno upozorenje.

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

Katalog tokena ​

Tablice u nastavku su ugovor v1: ključevi kataloga (TiptapifySkinColors / TiptapifySkinMetrics) su imena koja možete postaviti. Ćelije classic su CSS rezervne vrijednosti — vrijednost koju osnovna pravila crtaju kad nijedan skin ne nadjačava token.

Alatna traka / traka ​

tokenCSS varijablaclassicpastel
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 / razdjelnici ​

tokenCSS varijablaclassicpastel
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 uzdignutost 2 ²none
dividerWidth--tf-divider-width0px (razmaknica: margin 0 4px)1px
dividerHeight--tf-divider-height0px20px
dividerColor--tf-divider-colortransparentrgba(var(--v-border-color), var(--v-border-opacity))

¹ Izmjerena classic ćelija je normal — izvorna flex vrijednost Vuetifyja, koju nosi CSS rezervna vrijednost. Osnovna vrijednost kataloga 0px je ekvivalentna izričita duljina za nadjačavanja skina.

² Ćelije koje glase „Vuetify uzdignutost n“ su opisne: točan string trostruke sjene je izmjerena vrijednost u pohranjenom baseline fixtureu (src/skins/__fixtures__/skin-baseline.json) i ujedno je CSS rezervna vrijednost. Za groupShadow to je uzdignutost 2 (0 3px 1px -2px …, 0 2px 2px 0 …, 0 1px 5px 0 …) — pod groups: 'box' odjeljak je VBtnGroup (elevation="4"), a pravilo tokena boji okvir ovom trojkom preko vlastite uzdignutosti grupe: okvir posjeduje uzdignutost, spojeni potomci gumbi je ne nose.

Gumbi ​

Ovi tokeni koriste se samo pod buttons: 'quiet' — vidi Dohvatljivost.

tokenCSS varijablaquiet vrijednost (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 izbornici (odabir, prazan redak, slika) ​

tokenCSS varijablaclassicpastel
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 varijablaclassicpastel
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 uzdignutost 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 popisa (radijus, sjena, razmak popisa) klasičnog izbornika danas je pravilo skina — omotač tokena SkinScope nalazi se između Vuetifyjeva vlastitog pravila potomka i popisa, pa biblioteka te piksele vraća kao pravila skina koja čitaju gornje tokene. Pikseli stavki ostaju izvorni Vuetify (fixture mjeri Vuetifyjeve zadane vrijednosti): četiri tokena stavke su neaktivna dok je menu: 'vuetify'.

Dijalozi ​

tokenCSS varijablaclassicpastel
dialogShadow--tf-dialog-shadowVCard uzdignutost ²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-radiuszadano za 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č obruba uređivača) ​

tokenCSS varijablaclassicpastel
frameBorder--tf-frame-border1px solid rgba(var(--v-border-color), var(--v-border-opacity)), bez gornjeg rubapuni obrub
frameBg--tf-frame-bgtransparentrgb(var(--v-theme-surface))
frameShadow--tf-frame-shadownone0 1px 2px rgba(0, 0, 0, 0.05)

Pod oblikom ravne alatne trake okvir crta i razliku po strani (gornji rub i donji rub na 1,6× neprozirnosti obruba). Ta razlika pripada obliku, a ne tokenu: nije dohvatljiva tokenom.

Slike i ručke za promjenu veličine ​

tokenCSS varijablaclassicpastel
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) (odabrano)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%

Alatna traka slike prati zastavicu buttons: pod oblikom quiet dobiva quiet površinu — njezini razmaci od 2px/4px čitaju --tf-bubble-gap / --tf-bubble-padding s rezervnim vrijednostima izbornika slike; ne postoji poseban token za alatnu traku slike.

Nije u katalogu (područje izlaza za nuždu) ​

Neke dimenzije navedene u pregledu dizajna u v1 nemaju token u katalogu: visina alatne trake, širina okvira, radijus okvira, širina gumba, razmak gumba, širina dijaloga. Tablice kataloga gore su ugovor; za te piksele otvorite izlaz za nuždu i stilizirajte stabilne klase izravno.

Dohvatljivost ​

Ugovor, naveden jednom i poštovan u svakoj gornjoj skupini:

Token boji površinu samo kad oblik tu površinu aktivira; izvorni Vuetify vizuali stiliziraju se bojama Vuetify teme.

Po skupinama:

  • Alatna traka, okvir, okvir slike/ručke — 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' odjeljak je VBtnGroup (struktura iz 0.3.2: gumbi se spajaju, okvir nosi uzdignutost); pravila okvira boje ga tokenima grupe, a --tf-group-height drži ugovor od 32px s !important (Vuetifyjevo pravilo gustoće se bori protiv njega, kao u 0.3.2). Pod groups: 'flat' odjeljci su obični div nizovi (prozirni, bez sjene, radijus 0 je osnovica oblika).
  • Tanke crte razdjelnika (dividerWidth/Height/Color) — vidljiva tanka crta je mogućnost oblika groups: 'flat'; u obliku box razdjelnik je razmaknica nulte veličine (margin: 0 4px iz osnovnog pravila).
  • Razmak/praznine stavki — kompaktni niz 6px 8px ograničen je na ravnu traku (toolbar: 'flat'), praznine grupa na ravne odjeljke (groups: 'flat'); podignuta traka zadržava klasične metrike.
  • Gumbi (buttonFg, buttonHover*, buttonActive*, buttonRadius) — neaktivni pod buttons: 'vuetify': izvorni Vuetify gumbi crtaju vlastita stanja. Stilizirajte ih bojama Vuetify teme (primary, surface, …) — klasični oblici ih namjerno nasljeđuju.
  • Metrike stavki izbornika (menuItemRadius/Padding/MinHeight/ActiveBg) — neaktivne pod menu: 'vuetify'; Vuetify posjeduje te piksele stavki. Okvir popisa izbornika (radijus/sjena/razmak) crta skin u oba oblika (vidi napomenu o izborniku).
  • Meki okvir dijaloga (dialogActionBtn*, dialogFieldRadius, meki razmaci, nepostojeći razdjelnik) — crta se pod dialog: 'soft'; pod dialog: 'classic' dijalog je VCard s klasičnim razmacima.
  • Bubble okvir — izbornici odabira, praznog retka i slike su u oba oblika jedan red ikonskih gumba, obojen bubble tokenima; bubble im više ne mijenja oznake.

Nadjačavanje jednako osnovici kataloga ne emitira se — pod ne-klasičnim oblikom osnovicu oblika crta CSS rezervna vrijednost, pa „vraćanje“ klasične vrijednosti (npr. postavljanje buttonActiveBg natrag na rgb(var(--v-theme-primary)) pod buttons: 'quiet') tiho ne radi ništa. Da biste pod ne-klasičnim oblikom iznudili klasičnu vrijednost, postavite nadjačavanje koje se razlikuje od osnovice kataloga — ekvivalentan literal poput rgba(var(--v-theme-primary), 1) crta identično i preživljava preskakanje.

Tokeni namjerno nisu spojeni na izvorne putanje — to je ono što drži default identičnim klasičnom izgledu do na piksel, dok sve prolazi kroz katalog.

Recepti ​

Proširivanje pastela ​

Naslijedite quiet oblik i prebojite nekoliko površina. Registrirana imena rade na svakom 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; neizmijenjeni tokeni zadržavaju vrijednosti pastela — quiet neutralna boja gumba ostaje neutralna, mijenja se samo ono što nadjačate. Ovo je skin prikazan uživo na stranici primjera.

Proširivanje zadanog skina ​

Zadržite klasičnu strukturu, prilagodite boje:

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

extends: 'default' je implicitni korijen, pa se ključ može izostaviti: { toolbar: 'flat', … } također kreće od classic.

Potpuno prilagođeni ​

Bez extends, bez registracije: proslijedite objekt propu skin i slobodno postavite zastavice oblika i tokene. name stavlja vašu oznaku na korijensku klasu (vidi dolje); bez njega to 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 identitetna funkcija za tipizirane const definicije (ne ulazi ni u jedan registar). Zastavice oblika mogu se postavljati pojedinačno — svaka izostavljena zastavica ostaje classic.

Izlaz za nuždu: korijenska klasa ​

Svaki korijen uređivača nosi tiptapify-skin-{name} (tiptapify-skin-inline za inline objekt bez imena). Biblioteka nikad ne veže nijedno pravilo za ime 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 pisanje stilova:

klasagdje
tiptapify-skin-{name}korijen uređivača
tiptapify-shape-{flag}-{value} ×6korijen uređivača — razriješeni oblik
tiptapify-toolbar · tiptapify-toolbar-items · tiptapify-toolbar-group · menu-divideralatna traka i njezini odjeljci
tiptapify-quiet-buttonskorijeni alatne trake i bubble izbornika pod buttons: 'quiet'
tiptapify-bubble-row · tiptapify-bubble-dividerred svakog bubble izbornika i tanka crta između njegovih grupa
tiptapify-bubble-menu · floating-menu · tiptapify-image-menuredovi izbornika odabira, praznog retka i slike
tiptapify-btn--activepritisnuti gumb bubble izbornika (aktivna oznaka, plutanje slike)
tiptapify-menu · tiptapify-menu--softsadržaj preklapanja padajućeg izbornika, nositelj pravila mekog izbornika
tiptapify-skin-scopediv opsega tokena unutar teleportiranih izbornika
tiptapify-dialog · tiptapify-dialog--softkartica dijaloga i njezin meki nositelj
tiptapify-frameomotač obruba uređivača

Obrana od korisničke teme. Sloj skinova izgrađen je da preživi admin teme koje dodaju razmake i prebojavaju unutrašnjost Vuetifyja: pravila se ograničavaju klasama oblika/nositelja, koriste udvostručene klase (.v-list.v-list, .v-btn.v-btn.v-btn) radi specifičnosti i !important zadržavaju samo gdje se u slučaju admin teme iz osnovnog commita pokazao nužnim (skup obrane iz menu.css). Konvencije koje održavaju vaša vlastita nadjačavanja ispravnima na istom bojištu:

  • ograničite svoj CSS pod .tiptapify-skin-{name} (ili klasu oblika) — nikad na goli unutarnji Vuetify;
  • za dimenzije iz kataloga radije koristite tokene (--tf-*), a klase za ostalo;
  • uskladite udvostručenje pri nadjačavanju Vuetify komponenti (jedan .v-list-item gubi od pravila skina).

Vizualni ugovor (napomena za suradnike) ​

Izračunati stilovi ugrađenih skinova fiksirani su baseline fixtureom src/skins/__fixtures__/skin-baseline.json (13 odjeljaka × svijetlo/tamno): CSS sloj je gotov kad izmjerena matrica odgovara fixtureu. Napomene za čitanje fixturea:

  • Hover/active dimenzije ne mogu se iznuditi sa stranice, pa snimanje čita tekst deklaracije prvog pogođenog CSSOM pravila za svaki hover selektor i razrješava ga kroz probni element unutar korijena skina (polje resolved) — to je ugovor pobjedničkog pravila, a ne snimka stvarnog :hover.
  • _meta.selectors.menu bilježi tekst selektora prije SkinScope. Fixture je zamrznut (normativne vrijednosti), pa je to polje namjerno zastarjelo; bundle snimanja fiksira trenutni selektor.
  • Fixture nosi svoje odobrene promjene u _meta.notes: obnova VBtnGroup oznaka pod groups: 'box' od 2026-10-18 ponovno je izmjerila classic polja potomaka groupBtn/buttonsActive (spojeni kutovi, okvir posjeduje uzdignutost). Polja pastela nisu dirana — namjerna vizualna promjena ažurira samo zahvaćene classic ćelije, bilježi napomenu i isporučuje redak u CHANGELOG-u.

Vidi i ​

Objavljeno pod MIT licencom.