Token drift never announces itself. It starts with a single hardcoded value shipped under deadline pressure, and a year later the product has four slightly different grays for the same surface, with nobody able to say which one is canonical.
It arrives one shortcut at a time
One hex typed straight into a component. Then a spacing tweak that skips the scale. Then a shadow that lives in exactly one file. Each shortcut is reasonable on its own and invisible in review. Together they are drift, and drift compounds silently until a redesign forces you to count the damage.
You can feel it in the symptoms: colours that are almost but not quite the same, spacing that looks off by a pixel you cannot locate, a dark mode that is right on most screens and wrong on a few.
#2b2d31
#2b2d31
#2b2d31
#2b2d31
Drift is an architecture problem, not a discipline problem
Telling engineers to "just use the tokens" does not work, because drift is rarely carelessness. It is what happens when the token system cannot express what a screen actually needs. If there is no token for a hovered destructive action, someone will invent a value, and that invention is drift.
So the fix is not more willpower. It is a layered architecture where the right token always exists and always resolves to one place.
The three layers
Primitives define what values exist: the raw ramps and scales, named but opinion-free. Semantic tokens define what those values mean: surface, accent, danger, the intent behind a choice. Component tokens define where they apply: button background points at accent, card surface points at surface.
When each layer only references the layer below, a theme change is a swap at the primitive level that ripples upward automatically. Nothing hardcodes a value, so nothing can drift away from it.
Automate the boundary
Architecture removes the reason to drift; automation removes the opportunity. Sync tokens from the source of truth into code on every change, generated and versioned like any other dependency. Then lint component styles for raw values so a stray hex fails the pull request instead of surfacing in an audit two quarters later.
Once that pipeline exists, drift stops being a recurring cleanup project and becomes a build failure you fix in minutes. That is the difference between a system that erodes and one that compounds.
A drift-proof pipeline
- One source of truth for values (Figma variables or a tokens file), never a designer's memory.
- Generate platform tokens from it automatically on publish.
- Reference only semantic and component tokens in code, never raw values.
- Lint for hardcoded hex, px, and shadows so drift is caught in review.
- Version and review token changes exactly like code changes.
Discipline fights drift and loses. Architecture makes drift impossible to introduce.On design tokens
Catch drift where it is born
Drift is cheapest to fix at the moment it is typed and most expensive to fix once it has shipped and spread. That argues for moving the catch as early as possible: a lint rule that fails a raw hex in the editor and the pull request, before the value ever reaches a screen. The earlier the guardrail, the smaller the cleanup.
This is also what makes the fix durable. Architecture removes the reason to drift, but a lint removes the opportunity, and the two together turn a recurring cleanup project into a problem you simply stop being able to create.
Drift is not a moral failing on the team. It is a structural gap in the system. Close the gap with layers and a pipeline, and the problem stops being something you clean up and becomes something you cannot create.