Most teams start a design system by building a button. It is the obvious first move: everyone agrees a button is a component, everyone has forty of them scattered across the codebase, and consolidating them feels like progress. It is progress. It is also the part that ages worst.
The button you build in month one encodes a hundred small decisions — a radius, a focus ring, a disabled opacity, the exact grey of a secondary variant. If those decisions live inside the component, then every one of them is a merge conflict waiting for the day the brand changes.
What a token actually buys you
A token is a named decision. --primary is not “orange”; it is “the colour this product uses to
mean act now”. Naming it that way does three things at once:
- A rebrand becomes one edit. Change the value, not the forty components that reference it.
- Dark mode falls out for free. The name stays constant while the value flips per theme.
- Review gets easier. A pull request that adds
bg-orange-700is visibly wrong in a way thatbg-primarynever is, because the first one is a decision being made in the wrong place.
The third is the one people underestimate. A token architecture is mostly a review tool.
Three layers, each with one job
The arrangement we land on for nearly every project has three tiers, and the discipline is that no tier reaches past the one below it.
- Palette aliases name the brand on top of a raw scale. This is the only layer that ever sees a literal colour.
- Semantic variables map meaning onto that palette, once per theme.
--background,--foreground,--border. - The bridge exposes those variables to utility classes so markup can say
bg-backgroundwithout knowing which theme is active.
@theme {
--color-primary-700: var(--color-orange-700);
}
:root {
--primary: var(--color-primary-700);
}
@theme inline {
--color-primary: var(--primary);
}
Markup only ever touches the third layer. That is the whole rule, and it is enforceable by grep.
The test for whether something deserves a token is ownership, not inconvenience. If you could restyle it, it is a token. If it belongs to somebody else — another company’s logo, a partner’s brand colour — it is a fact, and it ships as a literal.
Where it breaks
Two ramps that are not parallel will catch you out. A card surface that sits above the page in dark mode may sit below it in light, and a component that hard-codes the dark relationship flattens to nothing when the theme flips. The fix is unglamorous: pair the two values on the element so they cannot drift apart, and check every surface against both ramps before assuming the relationship holds.
The other failure is subtler. Once a system has tokens, there is a pull toward adding one every time a value is slightly off. Resist it. A new stop in the palette should answer a different question, not a four-unit difference that only an eyedropper can see.






