Overview
Token architecture
Three tiers, two independent axes. Components only ever touch the top tier. Tokens resolve down through the layers to a raw OKLCH value.
Primitives
ref/*Raw values, named by hue + step. Never referenced by components.
--ref-blue-500, --ref-gray-300Brand
tool/*Per-tool accent. One tool per Figma brand mode.
--tool-accent, --tool-accent-strongSemantic
color/*What components consume. Aliases a primitive or the brand layer.
--color-bg-canvas, --color-text-primary, --color-brandTwo independent axes
Theme (light/dark) lives on the Semantic collection, driven by [data-theme]. Tool (which accent) lives on the Brand collection, driven by [data-tool]. They combine freely — dark theme + Tool Two accent is just data-theme="dark" data-tool="tool-two" on any subtree.
Bridge to shadcn
shadcn ships with its own variable names — --background, --primary, --muted-foreground. Efni's bridge.css assigns those to Efni tokens, so shadcn components inherit theming without edits.
/* bridge.css (excerpt) */
:root {
--background: var(--color-bg-canvas);
--foreground: var(--color-text-primary);
--primary: var(--color-brand);
--primary-foreground: var(--color-text-on-accent);
--accent: var(--color-bg-hover); /* NOT the brand */
--border: var(--color-border-default);
--ring: var(--color-focus-ring);
}Hard rules
- Only reference semantic tokens from components. Never a primitive, never a hex.
- shadcn's
--accentis a neutral hover surface, not the brand. Brand is--color-brand(mapped to shadcn--primary). - Text tokens use emphasis, not lightness (
primary,secondary,tertiary) so names stay correct across themes. - Turn on
font-variant-numeric: tabular-numswherever numbers align.