Tokens
A token is a named value — a hex code, a number of pixels, a font weight. We store the values once, give them paths, and reference the paths everywhere else.
Tokens are values
Most styling decisions are values: a colour, a length, a font size. The question is where the
values live. If they live inline at the call site (<div style={{ color: '#1C1917' }} />), every
consumer holds its own copy, and changing the colour means hunting through the codebase. If they
live in a flat object (palette.ink), there is one copy but no structure — no way to model the
difference between the colour ink and the foreground of a button.
Motif organises tokens as a tree. Scales at the top (colors, space, radii), nested as deeply
as the design needs underneath.
Every leaf has a path: colors.ink, colors.blue.500, space.4. That path is how the rest of the
library refers to the value.
Two layers, one tree
The values themselves are only half the story. A design system also has to say what a value is
for. The colour #1C1917 is the colour ink. But the foreground of a button is also the colour
ink — except in dark mode, when it's the colour paper.
So tokens come in two kinds. They live in the same tree.
Primitives are the palette. Literal hex codes, literal pixel counts. They don't change with the theme.
Semantics name an intent. They reference primitives by path, prefixed with $.
When the theme switches, only the semantic layer rebinds. The primitives stay put. A button still
asks for $colors.action.primary.bg, and that path resolves to a different blue.
References walk the tree
A $-prefixed string is a reference, not a value. The resolver walks the dotted path against the
active theme:
If a reference points at another reference, the resolver keeps walking until it hits a literal. That is how a semantic name can sit one indirection above the palette without the consumer code knowing or caring.
createTheme types the tree
A theme is a name plus a token tree:
createTheme is a thin factory. It does not transform the input — it narrows the type so
$-references against this theme autocomplete in your editor.
The returned object is exactly the input. The work happens at the type level:
lightTheme.tokens.colors.text.default is statically known, and <Box bg="$colors.surface.base" />
is checked against the theme's actual shape.
One tree, two renderers
Tokens are the canvas every renderer paints on. On web, motif emits each leaf as a CSS custom
property — $colors.surface.base becomes var(--colors-surface-base) — and switches values by
setting data-theme on <html>. On native, the same tree lives in memory; the runtime resolver
reads from it directly.
The two renderers never see each other's output. They both see the same tokens.