How to Detect Semantic Drift in Design Tokens
To detect semantic drift in design tokens, compare what each token is supposed to mean—not only whether its name and file format still match. A token can remain valid in a design file and a code package while its role, reference chain, binding, or rendered result has diverged.
The useful audit is cross-layer. It connects semantic definitions to the design-tool representation, code package, consuming component, and representative UI state. The result should be a discrepancy record that explains what differs, where it matters, and whether the team should correct, migrate, or deliberately preserve the difference.
Define drift as a meaning mismatch
A primitive token describes a value such as a color, type size, or spacing measurement. A semantic token describes the job that value performs: text for a secondary label, a surface for an error state, or a border for a selected control. Salt Design System describes semantic tokens in terms of roles such as error, attention, and negative change.
That distinction gives the audit its starting point. blue-600 and text-link are not interchangeable records. The first identifies a value; the second makes a promise about usage. Drift occurs when that promise changes or is interpreted differently, even if both layers still contain a token called text-link.
Treat three discrepancies separately:
- Value drift: the token resolves to a different primitive value.
- Reference-chain drift: the token points through a different alias or semantic layer.
- Role or context drift: the token still has a similar name but represents a different state, component need, or product decision.
The third category is the hardest to find with a file diff because the text can look almost identical while the design intent has changed.
Collect the comparison inputs
Before comparing values, establish which representations belong to the same audit. A useful inventory records each token’s name, semantic role, source layer, referenced token, resolved value, consuming component, design binding, code usage, supported modes, and review status.
The source layers may include:
- primitive tokens and their values;
- semantic tokens and role definitions;
- component-level tokens or component properties;
- design-tool variables and their mode or collection context;
- generated code packages and transformation outputs;
- rendered component states in the product.
Do not assume that one file is the complete source of truth. A design variable may exist without being bound to the property that appears in a component. A code token may be generated correctly from one package while a product imports an older package. A component may bypass the semantic token entirely with a local value.
A practitioner account describes extracting semantic tokens from code packages into JSON and comparing them manually with Figma. That is a useful operational pattern, but it is an individual workflow rather than measured evidence that comparison is complete or reliable (the practitioner account).
Compare meaning before values
Start with the role definition. Ask what the token is intended to control, which states it covers, and whether the role is global or limited to a component or product context. Then compare the reference chain and resolved value.
For each apparent match, check:
- Does the name refer to the same role in both layers?
- Does it point to the same semantic or primitive source?
- Does the reference resolve under the same theme, mode, or brand context?
- Is the value appropriate for the role, or merely numerically identical?
- Has one side collapsed a semantic layer into a raw value?
A name match is only an identity candidate. It is not proof of equivalence.
Consider this hypothetical audit finding: both design and code contain text-muted, but the design variable references a low-contrast neutral while the code package references a value intended for disabled text. The audit should not label the discrepancy a simple color mismatch. It should record the competing role definitions, compare the affected states, identify the components using each interpretation, and route the issue for an explicit decision.
This distinction also prevents a common false positive. Two tokens can use different names while preserving the same role because one representation follows a product naming convention and the other follows a package convention. Rename-only differences may need normalization, but they are not automatically semantic drift.
Check bindings and actual usage
Structured token data cannot tell you whether a design property is actually connected to the intended variable. Inspect representative components and states rather than assuming that a visible value proves a binding exists.
For each sample, record:
- the component and state being inspected;
- the design property, such as text, fill, border, icon, or focus ring;
- the variable or token bound to that property;
- the code property consuming the corresponding token;
- any local override, hard-coded value, or fallback;
- the mode, theme, or product context;
- the expected rendered result.
Include states that expose role differences: default, hover, selected, disabled, error, and high-contrast or alternate-theme conditions where relevant. A default state can look correct while an error state uses an unrelated semantic role or a disabled label inherits ordinary secondary text.
Sample strategically. A broad extraction catches more candidates but creates review noise. A focused set of shared components and meaningful states is easier to maintain, though it can miss rarely used contexts. The sample should expand when the token affects multiple products, accessibility-sensitive states, or a component with many overrides.
Figma frames semantic variables as part of reducing the distance between design and code, but that vendor framing does not establish that variables prevent drift. The practical implication is narrower: design and code should be inspected as connected representations, not as isolated token files.
Verify representative rendered states
Rendered verification is the point at which a semantic discrepancy becomes a product-relevant finding. Compare the same component, state, content condition, theme, and viewport across design and implementation. Record what differs and what remains uncertain.
Check more than color. A changed token can affect contrast, hierarchy, emphasis, focus visibility, density, state recognition, or the distinction between unavailable and secondary content. If the design and code use different values but produce an intentional product-specific result, record the exception instead of forcing equivalence.
Rendered checks are slower and less exhaustive than structured comparison, so they should confirm selected candidates rather than replace inventory work. They also cannot explain every cause. A visual difference may come from typography metrics, component logic, content, browser behavior, or an implementation constraint rather than token drift.
Classify and prioritize discrepancies
A useful discrepancy record should contain the token name, competing role definitions, source representations, affected components or products, evidence links or screenshots, suspected category, owner, status, and next review point.
Classify each finding as one of the following:
- Naming difference: representations use different names but preserve the same role.
- Value drift: the role is stable but the resolved value differs.
- Reference drift: the token points to a different alias, primitive, mode, or transformation output.
- Binding gap: a design property or code property is not connected to the intended token.
- Usage drift: the token is used in a context outside its defined role.
- Intentional exception: the difference is documented, scoped, and owned.
- Unknown: available evidence is not enough to determine equivalence.
Prioritize findings by consequence rather than by count. Shared components, multiple products, accessibility-sensitive states, public brand decisions, and changes that would be difficult to reverse deserve earlier review. A low-impact naming inconsistency may wait; a token that changes error, focus, or disabled-state meaning should not be buried in the same queue.
The remediation decision may be correction, compatibility mapping, migration, documentation, or acceptance of an exception. Do not assign a universal drift score unless the team has defined what the score measures and how it changes decisions. No validated taxonomy, threshold, or prioritization model is established here.
Know what automation cannot decide
Automated extraction and comparison can find missing names, changed values, broken references, duplicate roles, and likely unbound properties. It is less reliable at deciding whether a role is appropriate in context. A token named text-secondary may be reasonable for one component and misleading for another if the hierarchy or state differs.
Canonical naming can make comparisons easier, but aggressive normalization can hide legitimate product-specific exceptions. Rendered checks add context, but they are slower and still depend on choosing representative states. Central governance can reduce ambiguity, yet it can also delay a local decision that has a clear scope and owner.
Use automation to surface candidates and contextual review to resolve meaning, exceptions, and remediation. The audit is successful when it produces traceable decisions—not when every representation is forced into identical syntax.
For changes that require migration, the findings can inform how to plan a design token migration. For each approved exception or role decision, document design-system decisions so the next audit can distinguish deliberate variation from unexplained drift.