A token system is not a palette with nicer names. It is a contract between design and code that says: every visual decision lives in exactly one place, and everything downstream refers back to it.
Names are not tokens
Renaming #FF7A00 to ember-500 changes nothing on its own. You still have a raw value that a component can hardcode, and a hundred places that can drift from it. A token becomes useful only when it is a reference that resolves, not a label you paste.
The unit of value in a token system is the reference. brand-button-bg does not contain a color; it points at accent, which points at ember-500. Change the primitive and the button follows, because it never held a color of its own.
The three layers, and why order matters
Primitives answer what values exist: the raw ramps, named and opinion-free. Semantic tokens answer what they mean: surface, accent, danger, the intent behind a choice. Component tokens answer where they apply: button background, card surface, input border.
The rule that makes it hold is strict: each layer may reference only the layer beneath it. Components never touch primitives. Semantics never touch components. Break that rule once and you have reintroduced the coupling tokens were meant to remove.
Modes are just another axis
Light and dark, comfortable and compact, brand A and brand B: these are not separate token sets, they are modes of the same semantic layer. surface resolves to one primitive in light and another in dark. The component never knows which mode it is in, and never has to.
That is the whole payoff. A theme change becomes a swap at the primitive level, and it ripples up automatically through semantics and components to every screen.
A token system that holds
- One source of truth for values, never a designer's memory.
- Three layers, each referencing only the one below.
- Semantic names for intent, so screens survive rebrands.
- Modes as an axis of the semantic layer, not duplicate sets.
- Generated into code on publish, and linted so raw values fail review.
Where teams get the layers wrong
The most common mistake is letting a component reference a primitive directly, skipping the semantic layer because it is quicker in the moment. It works until the rebrand, when that one component ignores the new theme because it was pointing at a raw value the whole time. One shortcut, one screen that will not re-theme.
The second mistake is a semantic layer that just renames primitives one to one: surface equals gray-900 and nothing more. Semantics earn their place by encoding intent that can change per mode, not by being an alias with extra steps. If your semantic layer has no modes, it is not yet doing its job.
A token is not a value with a nicer name. It is a promise that the value lives somewhere else.On token architecture