Design Tokens
The variable layer every component reads from: how tokens are named and layered, how to consume them, and how to change one without breaking the system.
What tokens are for
A token is a named design decision. Components reference the name, never the raw value.
That indirection is the point. Change the token and every component that uses it changes with it. Hardcode the value and the change becomes a search-and-replace across the codebase, which is where drift starts.
Layers
Tokens are worth splitting into layers so that intent survives a redesign:
| Layer | Holds | Example |
|---|---|---|
| Primitive | Raw values with no meaning attached | A blue, a spacing step, a radius |
| Semantic | Roles that point at primitives | Body text colour, accent, surface, border |
| Component | Overrides scoped to one component | Card padding, button height |
Components consume the semantic layer. Reaching past it into primitives couples a component to a value rather than to a decision.
This system exposes its tokens as CSS custom properties on :root, with semantic
names such as --ink, --muted, --surface, --border and --accent for
colour, --font-display and --font-body for type, and --radius-card and
--radius-pill for shape.
Naming
- Name by role, not by appearance.
--accentsurvives a rebrand,--bluedoes not. - Keep the order consistent: category, role, then variant.
--accent-hoverreads the same way as--accent-contrast. - Use the same words everywhere. Pick
surfaceorbackgroundand never mix them. - Do not encode a number that will go stale.
--gray-400invites a--gray-450.
Using tokens
1.card {
2 background: var(--surface);
3 color: var(--ink);
4 border: 1px solid var(--border);
5 border-radius: var(--radius-card);
6}
7
8.card__meta {
9 color: var(--muted);
10}Rules:
- No hex values, font stacks or hardcoded radii inside a component stylesheet.
- Give
var()a fallback only where the token may genuinely be absent. A fallback everywhere hides a missing token instead of surfacing it. - Theme by reassigning tokens in a scope, not by overriding component rules:
Changing a token
A token change is a global change. Treat it as one.
- Confirm the change belongs at the token layer. A one-off need is a component decision, not a system one.
- Check contrast for every pairing the token participates in. See the accessibility guidelines for the ratios.
- Review the component previews. The catalog is the fastest way to see the blast radius.
- Record the change so consumers know why their page moved.
Adding a token
Add one when the same value appears in three or more places and represents a real decision. Before adding, check that an existing token does not already cover it. Two tokens with the same value and overlapping meaning are worse than none, since neither can be changed with confidence.
Anti-patterns
- A token used in exactly one place. That is a local variable wearing a costume.
- A token whose name describes its current value. It will be wrong after the next design change.
- Component CSS that reads primitives directly, bypassing the semantic layer.
- Two names for the same concept, kept because renaming felt risky.