The first generation of design tokens solved a real problem: stop hardcoding `#06b447` in forty different files. The second generation solves a problem that generation created — a flat list of colors doesn't tell a new engineer which one to use where, or what happens when the whole surface inverts.
Two layers, not one flat list
We now split tokens into primitives (the raw values — a specific green, a specific spacing scale) and semantic tokens (what that value means in context — "brand-accent," "surface-inverted"). The primitive rarely changes; the semantic layer is what a component actually references.
Primitive → semantic → component
- Primitives are named by what they are (green-500, space-4) — stable, rarely touched, and never referenced directly by a component.
- Semantic tokens are named by what they mean (surface-primary, text-muted, border-focus) — this is the layer components actually consume, and the layer a theme swap remaps.
- Component tokens (optional, for large systems) — a button-specific override that still traces back to a semantic token, for the rare case a component needs to diverge.
Why this is what makes theming actually work
A theme toggle — light, dark, or a client's full brand reskin — becomes a single remap at the semantic layer instead of a search-and-replace across every component. This site's own dark/light toggle works exactly this way: components reference semantic classes, and the inversion happens in one place, not per-component.
If changing a theme means editing more than one file, your tokens are still primitives being used directly by components — there's no semantic layer absorbing the change.
Naming is the actual hard part
"Blue-500" tells you nothing about when to use it. "Text-muted" tells a new designer exactly what job it does, even if they've never opened the file before. We spend more review time on token names than on the values themselves — a badly named token gets misused within a week.
2
layers we split every token system into: primitive and semantic
1 file
changed to fully re-theme a product, when the semantic layer is done right
40+
components a single semantic remap correctly reached in our largest system
“A token system's real test isn't the light mode launch. It's whether the fortieth component still uses the token correctly without anyone reminding them.”
Key Takeaways
- Split tokens into primitives (raw values) and semantic tokens (contextual meaning) — components should only ever reference the semantic layer.
- A working semantic layer turns a full theme change into a single remap, not a per-component search-and-replace.
- Token naming deserves as much review time as the values — a name that doesn't communicate its job gets misused fast.
- If a theme swap touches more than one file, the semantic layer isn't actually doing its job yet.
Where to start
Audit your five most-used components for hardcoded primitive values used directly. Wherever you find one, that's your first semantic token to define — start there, not with a full system rewrite.





