DTCG Design Tokens Without Vendor Lock-In
A graph-first DTCG migration preserves aliases and composite tokens, validates the stable format, generates CSS, and diffs semantic output before cutover.
DTCG design tokens provide a stable interchange format, but converting JSON keys is the easy part. A safe migration preserves value types, token aliases, semantic meaning, platform output, and the dependency graph that products already consume.
This tutorial migrates a realistic color, typography, spacing, shadow, and border set into the stable design token format. A dependency-free validator resolves references, detects cycles, and compares generated CSS before any vendor source is removed.
Inventory before writing DTCG design tokens
Start by freezing the current token source, generated artifacts, consuming packages, and screenshots or computed-style fixtures. Inventory primitive values, semantic names, component tokens, themes, modes, deprecated aliases, custom extensions, and vendor-only metadata. Record which fields influence output even if they are not tokens.
A proprietary design token format may encode type implicitly in a path or transform name; the stable DTCG shape makes types and values more explicit. Migration scope should include only tokens whose meaning is understood. Unknown values enter a quarantine list rather than being guessed into a standard type.
Runnable artifact: The validator transforms a small proprietary set, resolves an alias, checks a typed dimension, and rejects reference cycles. Its dtcg-token-migration.test.mjs receipt keeps the article's simplified boundary executable and reviewable.
Save the inspectable proof as dtcg-token-migration.test.mjs and run node dtcg-token-migration.test.mjs. Expected final line: PASS: DTCG aliases preserved.
import assert from "node:assert/strict";
const legacy={blue:"#3157ff",action:"{blue}",space:8};const dtcg={$type:"color",blue:{$value:"#3157ff"},action:{$value:"{blue}"},space:{$type:"dimension",$value:{value:8,unit:"px"}}};const resolve=(name,x,seen=new Set)=>{if(seen.has(name))throw Error("cycle");seen.add(name);const v=x[name].$value,m=typeof v==="string"&&v.match(/^\{(.+)\}$/);return m?resolve(m[1],x,seen):v};assert.equal(resolve("action",dtcg),legacy.blue);assert.deepEqual(dtcg.space.$value,{value:8,unit:"px"});console.log("PASS: DTCG aliases preserved");
Map value types without flattening semantics
Translate colors, dimensions, durations, numbers, fonts, shadows, strokes, gradients, and composite structures according to the stable format's definitions. Unit-bearing values should not become bare numbers; a font family should not be treated as an arbitrary string if its consumer expects a typed value. Groups can establish inherited type where appropriate, but local exceptions need explicit handling.
Preserve descriptions and deprecation notes in supported fields or named extensions. The point is interoperable meaning, not visually similar JSON. Build a table from each legacy type and transform to its destination representation plus validation rule.
- 1Inventory
Freeze graph + outputs
- 2Transform
Type + preserve aliases
- 3Diff
Compare semantic output
- 4Cut over
Move reversible cohorts
Treat token aliases as a dependency graph
Token aliases should remain references when they express design intent. A semantic action color pointing to a palette blue is different from copying the hex value, because future palette changes should travel through the relationship. Parse references into a directed graph, reject missing targets and cycles, and resolve a deterministic value for each output platform.
Review alias depth and cross-theme references rather than enforcing an arbitrary universal limit. When a legacy system uses transformed aliases, document whether the DTCG graph plus transformer extension can preserve that meaning or whether a deliberate token split is required.
Model typography, shadow, and border composites
Composite tokens reveal weak migrations because their subvalues combine distinct types. A typography token can include font family, weight, size, line height, and letter spacing; shadows can be arrays; borders combine color, width, and style. Create fixtures that exercise optional fields and platform mappings rather than test only colors and spacing.
If a consumer cannot accept the composite directly, transform it into named platform output while retaining one canonical token. Avoid creating several competing source representations merely to satisfy current build scripts. The design token migration should reduce ambiguity, not spread it across transforms.
Generate CSS through an explicit transform boundary
The canonical DTCG document is not itself a CSS variables file. A transformer resolves aliases, converts typed dimensions and colors, applies naming policy, and emits stable custom properties. Style Dictionary can participate in that pipeline, but its configuration and extensions remain versioned implementation code rather than hidden standard behavior.
Sort output deterministically and include a generated-file header with source and transformer versions. Keep platform naming separate from canonical token paths so a CSS naming change does not rewrite the source model or pretend to be a semantic design decision.
Diff semantic output before cutover
Compare resolved old and new values by consumer name, type, theme, and mode. Text diffs are useful but insufficient when ordering or formatting changes; normalize into a semantic table first. Then run computed-style fixtures and focused visual regression across representative components, including dark mode, density, high contrast, and deprecated aliases.
Classify every difference as expected migration, pre-existing inconsistency, transform defect, or blocked unknown. Zero diffs is not always the goal—some defects may be intentionally corrected—but every visible change needs an owner and release note.
| Legacy family | DTCG type | Key check | Output proof |
|---|---|---|---|
| Color | color | Color space + channels | CSS value |
| Spacing | dimension | Value + unit | Custom property |
| Action | alias | Target exists | Resolved color |
| Typography | composite | Member types | Platform fixture |
Cut over consumers in reversible cohorts
Publish the DTCG source and generated artifacts under a versioned package, then move a small consumer cohort before the entire design system. Keep old and new generation paths in dual-run mode long enough to compare outputs, but prevent authors from editing both sources. The canonical-authority flag changes only after source and production consumers agree.
Provide codemods or alias shims for renamed variables, with removal dates. Monitor missing variables, fallback usage, bundle changes, visual diffs, and support reports. Rollback restores the generated artifact and consumer binding without throwing away the migrated canonical graph.
Govern portability after the migration
Vendor neutrality is maintained, not achieved once. Keep the canonical file valid against the named stable DTCG specification; place vendor metadata in documented extensions; test at least one independent parser or the dependency-free reference validator; and preserve output fixtures. Review proposals that add tool-specific fields for whether they encode source meaning or temporary build convenience.
Track unresolved extensions and alias depth. When the standard or tooling changes, run the semantic diff suite before adopting it. The durable outcome is a graph whose meaning remains inspectable even if the current design-token tool is replaced.
Keep the standard and the transformers separate
The stable DTCG format report is the migration authority, the community group's FAQ clarifies scope, and Style Dictionary token documentation describes one consumer rather than the canonical format. Use the site's design-system foundations, Figma variable naming, token starter explanation, and migration-under-pressure field note to negotiate naming, design-tool translation, and rollout without letting any single exporter become the source of truth.
Migrate one alias knot before the whole library
Select a small family containing a primitive color, a semantic alias, a component alias, a typed dimension, a deprecated name, and one vendor extension. Convert it to the stable format, resolve both old and new graphs, and compare platform outputs byte-for-byte where stability is promised and semantically where formatting may change. DTCG design tokens reduce lock-in only when the canonical graph can be validated without the former vendor tool and when consumers can switch independently behind an explicit compatibility window.
Archive the original token path, converted node, resolved value, emitted platform value, and any extension preserved outside the stable core. That DTCG design tokens crosswalk gives designers and engineers a shared place to review semantic drift, instead of accepting a successful JSON parse as proof that color spaces, dimensions, composites, and aliases survived.
Test the crosswalk with one consumer that does not use the migration transformer, even if it is only a small validation script. This independent DTCG design tokens reader demonstrates that the canonical graph is genuinely portable and that vendor-specific metadata can be ignored without destroying the standard value or its reference relationships.
| Decision | Evidence retained | Stop condition |
|---|---|---|
| Inventory before writing DTCG design tokens | source digest, token count by family, alias edges, output targets, consumers, extensions, unknown fields, and migration owner | unrecognized data is silently dropped because it does not fit the destination example |
| Map value types without flattening semantics | legacy type, destination $type, normalized $value, unit conversion, extension policy, and a representative round-trip fixture | two tokens that render similarly lose distinctions required by another platform |
| Treat token aliases as a dependency graph | reference source, target, path, resolved typed value, cycle and missing-target checks, and intentional flattening exceptions | the migration replaces every alias with a literal and calls the output semantically equivalent |
| Model typography, shadow, and border composites | composite schema, required and optional members, resolved subvalues, target transform, serialized output, and unsupported-case behavior | a composite is stringified into opaque shorthand that downstream tools cannot inspect |
| Generate CSS through an explicit transform boundary | canonical source hash, transformer and configuration version, target platform, naming rule, emitted artifact hash, and warnings | tool-specific convenience fields become the only representation of token meaning |
| Diff semantic output before cutover | normalized old-new pair, semantic classification, affected consumers, visual fixture, approval, and rollback mapping | a clean JSON diff substitutes for platform rendering and component evidence |
| Cut over consumers in reversible cohorts | cohort, package versions, authority flag, compatibility aliases, output comparison, rollback command, and shim retirement date | teams can edit old vendor JSON and new DTCG files concurrently |
| Govern portability after the migration | conformance version, extension registry, independent validation, output fixtures, unresolved portability risks, owner, and next review | one vendor's successful import is treated as proof of ecosystem portability |
DTCG design tokens remain portable while types, aliases, extensions, and generated outputs stay inspectable. Revalidate the canonical graph before changing the standard version, transformer, or authority flag.