Hardcoded values are the tax that compounds
A hex code typed directly into a component is fine the first time. It's fine the tenth time too. Somewhere past that, a rebrand or a second tenant's theme requires finding every one of those values by hand, and inevitably missing a few — the classic sign of a design system that never had a token layer underneath it.
Tokens aren't about having fewer colors. They're about giving every value a name tied to its role, so changing the role changes every place it's used, automatically.
Three layers, each with one job
Primitive tokens: raw values
`blue-500`, `space-4`, `radius-lg` — the actual numbers and colors, with no opinion about where they're used. This is the only layer that changes per brand.
Semantic tokens: role, not value
`color-action-primary` points at `blue-500`. Components reference the semantic name, never the primitive directly — that indirection is what makes theming possible at all.
Component tokens: local overrides
`button-primary-bg` maps to `color-action-primary` by default but can diverge for one component without touching the semantic layer everything else depends on.
A theme is a value swap, not a rewrite
Dark mode, a white-label brand, a high-contrast mode — each is a different primitive layer pointed at the same semantic names. Components never know which theme is active.
Name by intent, not appearance
`color-danger`, not `color-red`. A future theme might express danger as orange — the name has to survive the palette changing under it.
Enforce it with lint, not memory
A stylelint rule banning raw hex/px values outside the token file turns "please use tokens" from a code-review nag into something CI catches automatically.
Multi-brand is the real stress test
Dark mode alone can hide a weak token architecture — two themes can be brute-forced with enough CSS overrides. A third brand, with its own primary color, radius scale and typography, is where the semantic layer either holds or the cracks show: components that assumed "primary is blue" somewhere in their markup, spacing that was eyeballed instead of tokenized, icons baked with a fixed color. Design for the third theme even when only shipping two.
The takeaway
Primitive tokens hold raw values, semantic tokens hold intent, and components reference only the semantic layer. That indirection is the entire trick — a new theme becomes a new set of values, not a new set of components.
Let's talk about your product