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.
“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.








