# 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](/schemas/kit-contract.json) · theme manifest schema [/schemas/theme.schema.json](/schemas/theme.schema.json) · fingerprint `kitContractHash` in `GET /v1/meta`. ## DOM anatomy Root: `data-mz-el=""`, one `data-` per variant axis (responsive values add `data--md` / `-lg`), `data-state="a b"`. Parts: `data-mz-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`
`data-spacing` = `none` `compact` `normal` `spacious`
`data-width` = `narrow` `normal` `full` | `bleed` | | `Container` | `container` | — | `data-width` = `narrow` `normal` `full` | — | | `Stack` | `stack` | — | `data-direction` = `column` `row`
`data-gap` = `none` `xs` `s` `m` `l` `xl`
`data-align` = `start` `center` `end` `stretch`
`data-justify` = `start` `center` `end` `between` | `wrap` | | `Grid` | `grid` | — | `data-cols` = `1` `2` `3` `4` `5` `6`
`data-gap` = `none` `xs` `s` `m` `l` `xl` | — | | `Box` | `box` | — | `data-surface` = `background` `surface` `surface-alt` `primary` `accent` `inverse`
`data-padding` = `none` `xs` `s` `m` `l` `xl`
`data-radius` = `none` `control` `card` `media` `pill` | `border` `shadow` | | `Heading` | `heading` | — | `data-size` = `xs` `s` `m` `l` `xl` `2xl`
`data-tone` = `default` `muted` `inverse`
`data-align` = `start` `center` `end` | — | | `Text` | `text` | — | `data-size` = `xs` `s` `m` `l`
`data-weight` = `normal` `medium` `bold`
`data-tone` = `default` `brand` `accent` `neutral` `danger` `success` `sale` `muted` `inverse`
`data-align` = `start` `center` `end`
`data-lines` = `1` `2` `3` | — | | `Prose` | `prose` | — | `data-width` = `narrow` `normal`
`data-align` = `start` `center` `end` | — | | `Button` | `button` | `label` `icon` | `data-variant` = `primary` `secondary` `ghost` `link`
`data-tone` = `brand` `accent` `neutral` `danger`
`data-size` = `s` `m` `l` | `disabled` `loading` `full` `pressed` | | `IconButton` | `icon-button` | `icon` | `data-size` = `s` `m` `l`
`data-tone` = `brand` `accent` `neutral` `danger` | `disabled` `expanded` | | `Link` | `link` | — | `data-tone` = `default` `muted` `inverse`
`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`
`data-ratio` = `auto` `1:1` `4:5` `4:3` `3:2` `16:9`
`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`
`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`
`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`
`data-align-y` = `start` `center` `end`
`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`: 40 - `themePresetsMax`: 8 - `themeSectionsMax`: 2 - `themeCssGzBytes`: 30720 - `kitCssGzBytes`: 36864 - `baseCssGzBytes`: 12288 - `customCssMaxBytes`: 20480 - `tokensInlineMaxBytes`: 6144 - `themePackMaxBytes`: 262144 - `themeFontFamiliesMax`: 4 - `themeFontFilesMax`: 8 - `themeFontFileMaxBytes`: 153600