Updated June 22, 2026 13 min read
Middle Workflow

Migrating Custom Design Tokens to Figma Variables

Build - structure tokens & Variables

Migrate CSS/SCSS/JSON tokens to Figma Variables with DTCG export — map primitives, resolve aliases, sync to code without breaking your design system.

Migrating custom design tokens into Figma Variables means replacing ad-hoc JSON, SCSS maps, and CSS custom properties with a single source of truth that exports directly to DTCG format — and that migration is the highest-ROI move a design system team can make in 2026. Custom token files drift from production the moment they’re hand-edited; Figma Variables stay bound to the canvas, propagate changes through alias chains automatically, and produce DTCG JSON that Style Dictionary and code generators consume without intermediate translation layers. The W3C Design Tokens Community Group maintains the DTCG specification that makes this pipeline interoperable across tools.

Related workflows: Figma Design Tokens Guide for Variables architecture and DTCG export, token workflow plugins for pipeline setup, and find untokenized values to audit what you’re migrating.

In short

  • Audit your custom token inventory before touching Figma — untokenized hex codes and magic numbers are your migration baseline.
  • Map primitives first (raw values), semantics second (alias chains) — reversing this order creates duplicate tokens.
  • Use DTCG JSON as the migration bridge format — it decouples Figma’s internal data from your code pipeline.
  • Animation tokens (duration, easing) go into a separate collection — mixing them with color/spacing creates mode conflicts.
  • Run a coverage audit after migration — Atomize scans for values that didn’t get tokenized during the transition.

Why custom token files break at scale

Custom token files — JSON blobs, SCSS variable maps, hand-maintained CSS custom properties — fail the moment two teams touch them. A developer updates blue/500 in the SCSS map but forgets the JSON export; a designer changes the hex in Figma without updating the code repo; the handoff meeting burns 20 minutes reconciling which value is authoritative. This is not a tooling problem — it is a single-source-of-truth problem. Figma Variables solve it by making the canvas file the source: a designer changes a variable value in Figma, the DTCG export picks it up, and the code pipeline rebuilds without manual synchronization steps.

The migration is not zero-cost. Teams with 200+ custom tokens spread across multiple files, frameworks, and platforms need a deliberate migration sequence. The rest of this guide lays out that sequence — audit, map, export, verify — with concrete steps for each phase. The Figma help center documents Variables capabilities that form the target state.

Phase 1: Audit your custom token inventory

Before creating a single Figma variable, inventory every token source in your codebase. Design tokens hide in more places than teams realize: CSS custom properties on :root, SCSS variable maps, Tailwind config files, JSON theme files, and inline var() calls in component styles. Each source needs to be located, extracted, and listed before migration begins — missing a source means that token stays untokenized after the migration.

Run an automated audit first. Atomize’s find untokenized values scanner detects raw hex codes, spacing values, and font sizes inside Figma files — values designers placed without creating a variable. For code-side tokens, grep your repository for CSS custom property declarations, SCSS $variable assignments, and JSON theme keys. List every token in a spreadsheet with columns: token name, current value, source file, and whether it has a Figma equivalent.

Common token hiding places

  • :root { } blocks in global CSS files — often the largest single token source
  • Tailwind theme.extend in tailwind.config.js — colors, spacing, font sizes, border radii
  • SCSS $_variables.scss partials — check for nested maps with map-get() access patterns
  • JSON theme files (tokens.json, theme.json) — often hand-synced with design, always drifting
  • Component-level CSS custom properties — var(--button-bg, #3B82F6) with hardcoded fallbacks
  • Animation keyframes with hardcoded ms values — these become duration tokens in the migration

Phase 2: Map primitives, then semantics

The most common migration mistake is mapping semantic tokens before primitives. A semantic token like color/text/primary references a primitive like color/gray/950 — you cannot create the alias chain until the primitive exists. Start with raw value tokens: every unique color hex, spacing pixel value, font size, border radius, and duration in your inventory. Create one Figma variable per unique value, not per usage — if #030712 appears in 14 places across your SCSS and CSS, you create one gray/950 variable, not 14.

Create primitives inside a dedicated Figma variable collection. Name the collection primitives and organize by category using slash-separated groups: gray/50, gray/100, blue/500, space/4, space/8, font/size/14, font/size/16, radius/sm, radius/md, duration/fast, duration/normal. The slash separator creates the group hierarchy Figma uses for scoping — keeping colors, spacing, typography, and animation tokens in separate groups prevents mode conflicts later.

/* Primitives collection — raw values, no aliases */
gray/50     →  #F8FAFC
gray/950    →  #030712
blue/500    →  #3B82F6
space/4     =  4px
space/8     =  8px
font/size/14 =  14px
radius/md   =  8px
duration/fast = 150ms
easing/default = cubic-bezier(0.4, 0, 0.2, 1)

Once primitives exist, create a second collection named semantic and alias every semantic token to its primitive. color/text/primary aliases gray/950 from the primitives collection. color/background/default aliases gray/50. space/component/gap aliases space/4. This alias-based architecture is what makes Figma Variables fundamentally different from flat custom token files: changing one primitive propagates through every semantic alias automatically.

/* Semantic collection — aliases to primitives */
color/text/primary        →  {gray/950}
color/background/default  →  {gray/50}
color/interactive/default  →  {blue/500}
space/component/gap       →  {space/4}
radius/button             →  {radius/md}
duration/transition        →  {duration/fast}

Phase 3: Export to DTCG and wire the code pipeline

DTCG (Design Tokens Community Group) JSON is the migration bridge format. It decouples Figma’s internal variable representation from whatever code pipeline consumes the tokens — CSS custom properties, TypeScript constants, Tailwind presets, or platform-specific formats. Export both the primitives and semantic collections as DTCG JSON. The export contains $type, $value, and alias references that downstream tools resolve into final platform values.

{
  "color": {
    "text": {
      "primary": {
        "$type": "color",
        "$value": "{color.gray.950}"
      }
    },
    "background": {
      "default": {
        "$type": "color",
        "$value": "{color.gray.50}"
      }
    }
  },
  "duration": {
    "transition": {
      "$type": "duration",
      "$value": "{duration.fast}"
    }
  }
}

Wire Style Dictionary to consume the DTCG JSON output. Style Dictionary resolves alias references, transforms values to platform-specific formats, and generates CSS custom properties, TypeScript constants, SCSS variables, or Android XML in a single build step. The key configuration decision: point Style Dictionary at the DTCG JSON file path as its input source, not at a hand-maintained token file. From this point forward, the DTCG export from Figma is the token source of truth — the hand-maintained custom files get archived.

{
  "source": ["tokens/primitives.json", "tokens/semantic.json"],
  "platforms": {
    "css": {
      "transformGroup": "css",
      "buildPath": "dist/css/",
      "files": [{
        "destination": "variables.css",
        "format": "css/variables"
      }]
    }
  }
}

Phase 4: Verify — audit what didn’t migrate

A migration is never complete on the first pass. Values get missed — a border color in a deeply nested component, a spacing value in an auto-layout gap, a font size inside a text style that wasn’t linked to a variable. Run a coverage audit after the migration to detect every value in the Figma file that still lacks a variable binding.

Atomize’s coverage audit scans the full file and reports untokenized fills, strokes, spacing values, font sizes, and border radii — grouped by page and component. Run this scan immediately after completing the variable mapping. Each hit is a migration gap: a value that existed in your custom token inventory but was missed during the Figma Variables transition. Bind those remaining values to the appropriate primitive or semantic variable.

Animation and duration tokens — a separate collection

Animation tokens — duration values, easing curves, and delay timings — require their own collection in Figma Variables. Mixing duration tokens into the primitives collection with colors and spacing creates mode conflicts: a dark mode switch should not change animation timings, but if they share a collection, Figma forces them to share modes. Create a dedicated motion collection with groups for duration, easing, and delay.

Token typeFigma typeExample valueCollection
DurationNumber150ms → 150motion
Easing curveStringcubic-bezier(0.4, 0, 0.2, 1)motion
DelayNumber300ms → 300motion
Opacity keyframeNumber0 → 1 fadeprimitives (opacity)

Figma Variables support number and string types natively — duration values become number variables (the unit is applied at export time by Style Dictionary transforms), and easing curves become string variables. The Material Design 3 motion tokens specification provides a useful reference model for naming duration and easing tokens consistently across a design system.

Coexisting with legacy code during migration

Large codebases cannot cut over to DTCG-exported tokens overnight. During the transition, run both pipelines in parallel: the new DTCG → Style Dictionary pipeline outputs to dist/tokens/v2/, while the legacy custom token pipeline continues outputting to the existing paths. Components adopt the new token variables incrementally — one component at a time, verified by visual regression tests. The parallel period typically lasts 2–4 weeks for a medium codebase, after which the legacy pipeline gets removed.

Token naming parity is the critical constraint during coexistence. If the legacy system uses --color-primary and the DTCG export produces --color-interactive-default, every component migration requires a naming shim. Design the DTCG variable names to match the legacy naming convention where possible, or accept that a one-time find-and-replace across the component tree is part of the migration cost.

Common migration mistakes

MistakeSymptomFix
Semantics before primitivesCannot create alias references — variables don’t exist yetBuild the primitives collection first; semantics reference primitives via aliases
One collection for everythingDark mode toggles change animation timingsSeparate collections by mode behavior: colors, spacing, motion, typography
Skipping the coverage auditUntokenized values persist in nested components for monthsRun Atomize’s audit scanner immediately after migration; fix every hit
Hand-editing DTCG JSON after exportJSON drifts from Figma — the exact problem migration was meant to solveTreat Figma as the source of truth; DTCG JSON is build output, never hand-edited
No visual regression testingBroken components discovered in productionRun visual regression tests (Chromatic, Percy, or Playwright screenshots) before merging the token migration branch

Final verdict - Token Migration

Migrating custom design tokens to Figma Variables is the single largest reliability improvement a design system team can ship in 2026. The audit → map → export → verify sequence takes 1–2 weeks for a medium token inventory (200–500 tokens) and pays back in eliminated sync meetings and zero-drift handoffs within the first month. The hard part is not the tooling — Figma Variables and DTCG are mature — but the discipline to audit thoroughly, map primitives before semantics, and run a coverage scan to catch every missed value. Teams that skip the audit step leave untokenized values behind; teams that run it ship a complete migration.

A medium inventory of 200–500 tokens takes 1–2 weeks: 1–2 days for audit and inventory, 2–3 days for primitive and semantic variable mapping, 1 day for DTCG export and Style Dictionary configuration, and 1 day for coverage audit and cleanup. Larger inventories (1000+ tokens) add 1–2 weeks for mapping. The audit and verification phases are the rate-limiting steps — variable creation in Figma is fast once the inventory is complete.
Yes — duration values are stored as Number variables and easing curves as String variables in a dedicated motion collection. Figma does not natively export units (ms, s), so the unit is applied at build time by Style Dictionary transforms. Keep animation tokens in a separate collection from colors and spacing to avoid mode conflicts when toggling between themes.
Figma Variables is the native token engine inside Figma files — it stores primitives, semantics, and alias chains. Figma Weave is a separate feature for creating interactive connections between frames for prototyping. The two serve different purposes: Variables for design token management, Weave for interactive prototypes. Migrate tokens to Variables; use Weave for prototyping flows.
The legacy token files get archived after the migration is verified. During the transition (2–4 weeks), both pipelines run in parallel: the new DTCG → Style Dictionary pipeline outputs to a new path, and the legacy pipeline continues outputting to the existing paths. Components adopt the new tokens incrementally. Once every component has been migrated and visual regression tests pass, the legacy token files and pipeline are removed.
Map legacy names to new DTCG names in a migration alias file consumed by Style Dictionary. If the legacy system uses --color-primary and the DTCG export produces --color-interactive-default, add an alias: color/primary{color.interactive.default}. This alias layer lets components reference the old name while the underlying value comes from the new DTCG source. Remove the aliases after all components have been updated to use the new names.
Publish variables from a dedicated token library file as a Figma library. Other files consume the published variables — they cannot edit them, which is the intended behavior. The token library file is the single source of truth. Export DTCG JSON from this library file only. If different products need different token subsets, use separate semantic collections that alias the same shared primitives.
Partial automation is possible. Atomize can scan Figma files and suggest variable bindings for detected values, and scripts can parse existing JSON/SCSS token files and generate a variable mapping spreadsheet. But the decisions — which values to merge, how to name groups, whether to alias or duplicate — require human judgment about your design system's intent. Automate the detection and mapping suggestions; review and approve them manually.
DTCG (Design Tokens Community Group) is the W3C community specification for design token file format. It standardizes how token names, values, types, and alias references are represented in JSON. Using DTCG means downstream tools — Style Dictionary, Theo, Token Transformer — consume your tokens without custom parsers. Custom export formats require maintaining a parser for each consumer; DTCG is the interoperability standard that eliminates that maintenance cost.
See all

Follow us on every platform