Token Architecture
Design token taxonomy — tiers, naming dictionary, theme roots, and derivation patterns
Token Tiers
Theme Roots Primitives Semantic Component
--ui-color-primary --ui-color-* --ui-color-text --ui-button-bg
--ui-unit --ui-space-* --ui-color-bg --ui-input-height
--ui-radius-base --ui-row-* --ui-color-border --ui-card-radius
--ui-font-sans --ui-font-size-* --ui-opacity-* --ui-alert-bg
--ui-radius-* --ui-overlay-*
<!-- Tokens are organized into four tiers. Theme roots feed primitives, primitives feed semantic tokens, and semantic tokens feed component tokens. -->
<pre>
Theme Roots Primitives Semantic Component
--ui-color-primary --ui-color-* --ui-color-text --ui-button-bg
--ui-unit --ui-space-* --ui-color-bg --ui-input-height
--ui-radius-base --ui-row-* --ui-color-border --ui-card-radius
--ui-font-sans --ui-font-size-* --ui-opacity-* --ui-alert-bg
--ui-radius-* --ui-overlay-*
</pre>
Naming Dictionary
| Prefix | Scales | Example |
|---|---|---|
| unit | singleton | --ui-unit |
| row | 1–6 | --ui-row-2 |
| space | px, quarter, half, 0–8 | --ui-space-2 |
| color | open-ended | --ui-color-primary |
| font | sans, mono | --ui-font-sans |
| font-size | xs–4xl | --ui-font-size-lg |
| font-weight | normal, medium, semibold, bold | --ui-font-weight-bold |
| line-height | open-ended | --ui-line-height-md |
| letter-spacing | display, body, caps | --ui-letter-spacing-caps |
| radius | base, sm, md, lg, full | --ui-radius-md |
| border-width | sm, md, lg | --ui-border-width-sm |
| shadow | sm, md, lg | --ui-shadow-lg |
| z-index | base–debug (10 levels) | --ui-z-index-modal |
| duration | instant–slower | --ui-duration-fast |
| ease | default, in, out, in-out | --ui-ease-out |
| opacity | disabled, loading | --ui-opacity-disabled |
| overlay | bg, bg-light, bg-blur, bg-subtle | --ui-overlay-bg |
| heading | open-ended | --ui-heading-1-size |
| body / body-sm | open-ended | --ui-body-weight |
| caption / lead / eyebrow | open-ended | --ui-caption-size |
| input | open-ended | --ui-input-height |
<!-- Every --ui-* token must use a prefix from this dictionary. The lint enforces it. -->
<table class="ui-table">
<thead>
<tr>
<th>Prefix</th>
<th>Scales</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr><td>unit</td><td>singleton</td><td>--ui-unit</td></tr>
<tr><td>row</td><td>1–6</td><td>--ui-row-2</td></tr>
<tr><td>space</td><td>px, quarter, half, 0–8</td><td>--ui-space-2</td></tr>
<tr><td>color</td><td>open-ended</td><td>--ui-color-primary</td></tr>
<tr><td>font</td><td>sans, mono</td><td>--ui-font-sans</td></tr>
<tr><td>font-size</td><td>xs–4xl</td><td>--ui-font-size-lg</td></tr>
<tr><td>font-weight</td><td>normal, medium, semibold, bold</td><td>--ui-font-weight-bold</td></tr>
<tr><td>line-height</td><td>open-ended</td><td>--ui-line-height-md</td></tr>
<tr><td>letter-spacing</td><td>display, body, caps</td><td>--ui-letter-spacing-caps</td></tr>
<tr><td>radius</td><td>base, sm, md, lg, full</td><td>--ui-radius-md</td></tr>
<tr><td>border-width</td><td>sm, md, lg</td><td>--ui-border-width-sm</td></tr>
<tr><td>shadow</td><td>sm, md, lg</td><td>--ui-shadow-lg</td></tr>
<tr><td>z-index</td><td>base–debug (10 levels)</td><td>--ui-z-index-modal</td></tr>
<tr><td>duration</td><td>instant–slower</td><td>--ui-duration-fast</td></tr>
<tr><td>ease</td><td>default, in, out, in-out</td><td>--ui-ease-out</td></tr>
<tr><td>opacity</td><td>disabled, loading</td><td>--ui-opacity-disabled</td></tr>
<tr><td>overlay</td><td>bg, bg-light, bg-blur, bg-subtle</td><td>--ui-overlay-bg</td></tr>
<tr><td>heading</td><td>open-ended</td><td>--ui-heading-1-size</td></tr>
<tr><td>body / body-sm</td><td>open-ended</td><td>--ui-body-weight</td></tr>
<tr><td>caption / lead / eyebrow</td><td>open-ended</td><td>--ui-caption-size</td></tr>
<tr><td>input</td><td>open-ended</td><td>--ui-input-height</td></tr>
</tbody>
</table>
Theme Root Tokens
:root {
--ui-color-primary: oklch(0.6 0.2 260); /* brand hue */
--ui-color-neutral: oklch(0.5 0.01 260); /* neutral tint */
--ui-unit: 8px; /* grid base */
--ui-radius-base: 0.5rem; /* corner radius */
--ui-font-sans: system-ui, sans-serif; /* body typeface */
--ui-font-mono: monospace; /* code typeface */
}
<!-- Theme roots are the small set of tokens you override to create a brand theme. Everything else derives from them. -->
<pre>
:root {
--ui-color-primary: oklch(0.6 0.2 260); /* brand hue */
--ui-color-neutral: oklch(0.5 0.01 260); /* neutral tint */
--ui-unit: 8px; /* grid base */
--ui-radius-base: 0.5rem; /* corner radius */
--ui-font-sans: system-ui, sans-serif; /* body typeface */
--ui-font-mono: monospace; /* code typeface */
}
</pre>
Derivation
/* color-mix(): shade derivation from --ui-color-primary */ --ui-color-primary-light: color-mix(in oklch, var(--ui-color-primary), white 40%); --ui-color-primary-dark: color-mix(in oklch, var(--ui-color-primary), black 30%); --ui-color-primary-subtle: color-mix(in oklch, var(--ui-color-primary), transparent 85%); /* calc(): radius derivation from --ui-radius-base */ --ui-radius-sm: calc(var(--ui-radius-base) * 0.5); --ui-radius-md: var(--ui-radius-base); --ui-radius-lg: calc(var(--ui-radius-base) * 1.5); --ui-radius-full: 9999px; /* calc(): spacing from --ui-unit */ --ui-space-1: var(--ui-unit); /* 8px */ --ui-space-2: calc(var(--ui-unit) * 2); /* 16px */
<!-- Tokens use color-mix() and calc() to derive values from roots. Change one root and dozens of tokens update automatically. -->
<pre>
/* color-mix(): shade derivation from --ui-color-primary */
--ui-color-primary-light: color-mix(in oklch, var(--ui-color-primary), white 40%);
--ui-color-primary-dark: color-mix(in oklch, var(--ui-color-primary), black 30%);
--ui-color-primary-subtle: color-mix(in oklch, var(--ui-color-primary), transparent 85%);
/* calc(): radius derivation from --ui-radius-base */
--ui-radius-sm: calc(var(--ui-radius-base) * 0.5);
--ui-radius-md: var(--ui-radius-base);
--ui-radius-lg: calc(var(--ui-radius-base) * 1.5);
--ui-radius-full: 9999px;
/* calc(): spacing from --ui-unit */
--ui-space-1: var(--ui-unit); /* 8px */
--ui-space-2: calc(var(--ui-unit) * 2); /* 16px */
</pre>