A token is a decision with a name
The problem a name solves
A value used in forty places is forty opportunities to disagree. Somebody types 23 instead of 24. Somebody uses a slightly different blue because they picked it off a screenshot. Six months later the design contains four greys that were meant to be one, and nobody can change the brand colour without a search that will miss three instances.
A token is a design decision given a name, so that the decision exists in one place and every use refers to it.
:root {
--space-4: 1.5rem;
--colour-action: oklch(0.55 0.18 255);
--radius-md: 8px;
--text-lg: 1.25rem;
}That is the entire mechanism. Everything else in this lesson is about which decisions deserve names and how to structure them.
Two layers, and why the second one matters
The important structural idea is that tokens come in two kinds, and collapsing them is the commonest mistake.
Primitive tokens are the raw scale. They describe values and nothing else.
--blue-100: oklch(0.93 0.04 255);
--blue-500: oklch(0.55 0.18 255);
--blue-700: oklch(0.42 0.16 255);
--grey-900: oklch(0.22 0.01 255);Semantic tokens describe jobs, and they point at primitives.
--colour-action: var(--blue-500);
--colour-action-hover: var(--blue-700);
--colour-text-primary: var(--grey-900);
--colour-surface-raised: var(--grey-50);Components use only the semantic layer. Nothing in a button refers to --blue-500; it refers to --colour-action.
The payoff arrives the first time somebody asks for a dark theme, a rebrand, or a white-label version for a client. You remap the semantic layer — a dozen lines — and every component follows. Without the second layer you are editing every component, and you will miss some.
@media (prefers-color-scheme: dark) {
:root {
--colour-action: var(--blue-300);
--colour-text-primary: var(--grey-100);
--colour-surface-raised: var(--grey-800);
}
}Note what changed: only the mapping. The primitives are the same colours; they are simply assigned different jobs.
What deserves a token
The categories that reliably earn one:
- Colour — the full ramps, plus semantic roles.
- Spacing — the scale from the spacing block.
- Type — sizes, line heights, weights, family stacks.
- Radius — two or three values, as discussed in the shape lesson.
- Shadow — a small set, all agreeing about the light direction.
- Duration and easing — from the motion lesson.
- Border width — usually just 1px and 2px, and worth naming so a focus ring is consistent.
- Breakpoints, if you use media queries.
The test for anything else: will this value appear again, and would I want it to change everywhere at once? If both are yes, name it. If either is no, leave it inline. A one-off 37px offset in a single illustration is not a token, and making it one adds a name nobody will ever look up.
What tokens do not do
Be clear about this, because token systems attract more enthusiasm than they deserve. Tokens make a design consistent and changeable. They do not make it good.
A badly proportioned layout built entirely from tokens is a badly proportioned layout that is easy to restyle. Every judgement from the earlier blocks — hierarchy, grouping, measure, contrast — still has to be made, and a token system will happily encode a bad decision and propagate it to two hundred screens with perfect consistency.
That is worth saying plainly because teams reach this stage and feel that the design work is now done. It is the opposite: the tokens are the vocabulary, and what you say with them is still entirely up to you.
The alias trap
There is one way this structure goes wrong, and it is worth naming because it looks like good practice while it happens.
Somebody creates a semantic token for every single use, so the file fills with entries like --colour-settings-page-heading and --space-profile-card-top. Each is used once. The layer has stopped being a set of roles and become a list of places.
The symptom is that adding a screen requires adding tokens. A semantic layer should be roughly stable while the number of screens grows, because roles recur — text, muted text, surface, raised surface, border, action, danger — and screens do not create new ones. If yours grows linearly with the product, the names are describing locations rather than jobs.
The repair is to ask, for each token, what would break if this were used somewhere else. If the answer is nothing, the token was a location and should be replaced by the role it actually plays.
Tokens for things that are not colours
Spacing, radius and type are the obvious ones. Three that get forgotten and cause visible inconsistency:
- Border width. Usually 1px and 2px. Naming them means a focus ring and a selected state agree without anybody checking.
- Z-index. A small named ladder — dropdown, sticky header, modal, toast — prevents the arms race where every new overlay is given 9999 and the next one 10000.
- Maximum widths. The measure cap from the type block, the content column, the modal width. These are decisions that recur and get re-guessed constantly.
Where they live
For most work, a single CSS file of custom properties is enough, needs no build step, and works in every browser. That file is a design system, and for a great many projects it is the correct amount of one.
Larger setups keep tokens in a platform-neutral format — usually JSON — and generate CSS, Swift, Kotlin and Android XML from the same source, so a colour changed once reaches the web app, the iOS app and the Android app. Style Dictionary is the common open-source tool for that transformation, and it is worth knowing the pattern exists before you need it.
Do not start there. Start with the CSS file.
The one thing to keep
A token is a named design decision, and the structure that matters is two layers — primitive values and semantic roles pointing at them — because retheming means remapping the semantic layer rather than editing every component.
Before you move on
A team defines tokens such as --blue-500 and uses them directly in every component. When a dark theme is requested, they find themselves editing each component. What structural change would have prevented it?
Pick the one you would defend. Nobody sees your answer.