Skip to content

Design tokens

Tokens are the mechanism that turns the design system from a document into something a build consumes. They are the design-system equivalent of generating a client from a specification.

Everything on this page follows from one rule:

One direction of flow. A hand-edit downstream is a bug in the source, not a local fix.

Layer Example Changes when Referenced by
Primitive purple-600: #7c3aed, space-4: 16px The palette or scale is redesigned The semantic layer only
Semantic color-brand, color-danger, space-inset-md The meaning of something changes Components
Component button-primary-background That component is redesigned That component only

Most systems ship the first layer and stop. That is the mistake that makes a rebrand a breaking change: consumers depend on purple-600 being purple, so the day it is not, either the name lies or every consumer edits.

The middle layer is where the value is. It is also the layer that requires actual design thinking — deciding that color-danger and color-warning are different roles, and what each is for, is a harder and more useful conversation than picking two reds.

Figma variables
│ export
tokens.json ── the source of truth, in version control, reviewed
│ transform
├─> CSS custom properties (the web application)
├─> a TypeScript module (type-safe access, autocomplete)
├─> platform formats (iOS, Android, if they exist)
└─> documentation (the tables on /design-system)

Every arrow is a build step. None of them is a person copying a value.

The transform step is usually Style Dictionary or an equivalent; the specific tool matters far less than the property it provides, which is that the outputs cannot disagree with the source because they are derived from it.

What makes it a contract rather than a config file

Section titled “What makes it a contract rather than a config file”

Three things, and a token pipeline missing any of them is just a build step:

It is reviewed. tokens.json changes go through review like any other interface change, by both design and engineering. A token change is a change to the visual contract, and it is invisible in a screenshot until it is everywhere.

It is enforced downstream. A lint rule rejects raw colour, spacing, radius and type-size literals outside the theme layer. Without this, the pipeline is optional, and an optional pipeline is used by the people who were going to be careful anyway.

Breaking changes are handled as such. Removing or renaming a semantic role is a breaking change to every consumer. It gets a deprecation and a codemod, not a commit message.

Worth stating, because over-tokenising is a real failure and a costly one.

Tokens carry values with meaning. They do not carry layout, behaviour, composition, or anything conditional. A token called card-padding-when-compact-and-nested is not a token; it is a component decision that escaped into the wrong layer.

The test: could a designer change this value alone and have it be correct everywhere it applies? If the answer needs a “well, except when…”, it belongs in a component.

The one-way mirror. Figma exports to code, and a value changed in code never goes back. The two drift, both sides keep citing theirs, and eventually the generated file gets edited “just this once”. See keeping them aligned.

The token soup. Every value in the product becomes a token, including the ones used once. The system is now a second, worse language for CSS, and nobody can find the ten tokens that matter among the four hundred that do not.

The unenforced pipeline. The generation works perfectly and nothing stops a developer writing #7c3aed. Adoption then depends on diligence, which is another way of saying it depends on how busy people are.