Element Contract v1 — themes are UI kits
A Mozaik theme is three things (ADR 0031): its own TSX implementation of this contract (a *kit*), its variables, and its page skeletons. Sections are platform-owned and compose only these elements, so content never lives in a theme and every extension works with every theme. FULL kits implement the 24 required elements in TSX (export const kit = {…} satisfies Kit); SKIN themes ship CSS + variables + skeletons over a parent kit (extends). All theme CSS targets the DOM anatomy below with attribute selectors only — classes are never a contract.
Machine-readable: /schemas/kit-contract.json · theme manifest schema /schemas/theme.schema.json · fingerprint kitContractHash in GET /v1/meta.
DOM anatomy
Root: data-mz-el="<element>", one data-<axis> per variant axis (responsive values add data-<axis>-md / -lg), data-state="a b". Parts: data-mz-part="<part>". Sections: data-mz-section, data-mz-slot. Island hooks (data-mz-*) pass through untouched and are never selectable in theme CSS.
| element | data-mz-el | parts | axes | states |
|---|---|---|---|---|
Section | section | head body | data-surface = background surface surface-alt primary accent inverse<br>data-spacing = none compact normal spacious<br>data-width = narrow normal full | bleed |
Container | container | — | data-width = narrow normal full | — |
Stack | stack | — | data-direction = column row<br>data-gap = none xs s m l xl<br>data-align = start center end stretch<br>data-justify = start center end between | wrap |
Grid | grid | — | data-cols = 1 2 3 4 5 6<br>data-gap = none xs s m l xl | — |
Box | box | — | data-surface = background surface surface-alt primary accent inverse<br>data-padding = none xs s m l xl<br>data-radius = none control card media pill | border shadow |
Heading | heading | — | data-size = xs s m l xl 2xl<br>data-tone = default muted inverse<br>data-align = start center end | — |
Text | text | — | data-size = xs s m l<br>data-weight = normal medium bold<br>data-tone = default brand accent neutral danger success sale muted inverse<br>data-align = start center end<br>data-lines = 1 2 3 | — |
Prose | prose | — | data-width = narrow normal<br>data-align = start center end | — |
Button | button | label icon | data-variant = primary secondary ghost link<br>data-tone = brand accent neutral danger<br>data-size = s m l | disabled loading full pressed |
IconButton | icon-button | icon | data-size = s m l<br>data-tone = brand accent neutral danger | disabled expanded |
Link | link | — | data-tone = default muted inverse<br>data-underline = always hover none | external current |
Icon | icon | — | data-size = s m l | — |
NavMenu | nav-menu | list item link sub toggle | data-orientation = horizontal vertical | current open |
Image | image | — | data-fit = cover contain<br>data-ratio = auto 1:1 4:5 4:3 3:2 16:9<br>data-radius = none media | priority |
Gallery | gallery | main thumbs thumb dots | data-layout = thumbs-below thumbs-side stacked carousel | — |
Price | price | now compare | data-size = s m l<br>data-align = start center end | sale |
Badge | badge | — | data-tone = neutral sale new info success danger | — |
ProductCard | product-card | media badges body title price soldout | data-layout = vertical horizontal<br>data-title = below overlay | unavailable |
Quantity | quantity | dec input inc | data-size = s m l | disabled |
ChoiceGroup | choice-group | legend option label | data-variant = chips list | — |
Drawer | drawer | scrim panel head close body title | data-side = start end bottom | open closed |
Field | field | label control hint error | — | invalid required |
Input | input | — | data-size = s m l | invalid disabled |
Select | select | — | data-size = s m l | invalid disabled |
Split *(optional)* | split | a b | data-ratio = 1:1 1:2 2:1<br>data-align-y = start center end<br>data-gap = none xs s m l xl | reverse |
Placeholder *(optional)* | placeholder | — | data-ratio = auto 1:1 4:5 4:3 3:2 16:9 | — |
CollectionCard *(optional)* | collection-card | media title count | data-variant = card tile circle | — |
Breadcrumbs *(optional)* | breadcrumbs | item link current | — | — |
Textarea *(optional)* | textarea | — | — | invalid disabled |
Checkbox *(optional)* | checkbox | box label | — | checked invalid disabled |
Accordion *(optional)* | accordion | item trigger panel | — | open |
KeyValueList *(optional)* | key-value-list | row key value | — | emphasis |
Divider *(optional)* | divider | — | data-spacing = none xs s m l xl | — |
Logo *(optional)* | logo | text image | data-size = s m l | — |
Notice *(optional)* | notice | icon title body dismiss | data-tone = info success warning danger | dismissible |
Tabs *(optional)* | tabs | list tab panel | — | selected |
Dialog *(optional)* | dialog | scrim panel head close body title | — | open closed |
Pagination *(optional)* | pagination | prev next page | — | current disabled |
Table *(optional)* | table | head row cell | — | — |
Video *(optional)* | video | poster play | — | — |
Marquee *(optional)* | marquee | track item | data-speed = slow normal | — |
Countdown *(optional)* | countdown | unit value label | — | done |
Icons (29, every FULL kit ships all): cart menu close search chevron-down chevron-up chevron-left chevron-right arrow-right user check star truck shield return whatsapp phone mail pin heart minus plus info warning error external card box headset.
Breakpoints
min-width only: sm = 480 px · md = 768 px · lg = 1024 px. Theme CSS may use no other widths.
Protected chrome
Platform-composed, never kit-implemented, pinned in base.css with !important (which no theme/skin/owner CSS may use): consent-banner consent-text consent-accept consent-reject identity-block identity-row identity-key identity-value builder-overlay. The KVKK consent banner and the 6563 identity block cannot be hidden or weakened by CSS.
Core tokens (Layer 0)
Platform ids every theme resolves; the owner floor is always editable in the builder: color.primary color.accent color.background color.text font.heading font.body font.baseSize radius.control radius.card radius.media shadow layout.sectionSpacing layout.containerMax. All ids: color.background color.surface color.surfaceAlt color.text color.textMuted color.primary color.onPrimary color.accent color.onAccent color.border color.sale color.success color.danger color.ring color.overlay font.heading font.body font.baseSize font.scale layout.containerMax layout.sectionSpacing layout.gutter radius.control radius.card radius.media shadow button.style button.radius motion. Theme variables (Layer 1, variables.json, ≤40) are emitted as --mzt-*.
Storefront zones
Extensions contribute kit-trees (JSON element trees) into closed zones rendered by platform sections, placed by the store owner: header.utility header.below home.top home.bottom product.gallery.after product.below-buybox product.after-description product.bottom collection.before-grid collection.after-grid cart.top cart.bottom footer.above page.top page.bottom.
Limits
themeVarsMax: 40themePresetsMax: 8themeSectionsMax: 2themeCssGzBytes: 30720kitCssGzBytes: 36864baseCssGzBytes: 12288customCssMaxBytes: 20480tokensInlineMaxBytes: 6144themePackMaxBytes: 262144themeFontFamiliesMax: 4themeFontFilesMax: 8themeFontFileMaxBytes: 153600