Skip to content
Back to Build Tutorials

Design Token Pipeline

Build a CI pipeline that transforms tokens from Figma into CSS/Tailwind/iOS/Android formats.

Why

Design tokens are the contract between design and engineering — if they drift, every screen drifts. A token pipeline makes Figma the single source of truth and every platform a generated consumer: change a color in Figma, and CSS, Tailwind, iOS Swift, and Android XML all update in one PR. Without the pipeline, each platform maintains its own copy of the tokens, and a brand refresh becomes a 3-week cross-team project with inevitable inconsistencies. The pipeline also enforces naming conventions (color.brand.primary, not color.blue) and prevents invalid values (a 13px font size that isn't in the scale).

When

When you ship to ≥2 platforms or have ≥20 tokens. Takes 3-4 h to set up Style Dictionary. Skip for single-platform small apps.

How

1. Define tokens in Figma using the Tokens Studio plugin, synced to a tokens.json in the repo
   - Structure: { color, typography, spacing, radius, shadow, motion }
   - Use semantic names: color.surface.primary, not color.white

2. Install Style Dictionary and write a config that emits all platforms
   - CSS: --color-surface-primary
   - Tailwind: extend.colors.surface.primary
   - iOS: UIColor extension; Android: colors.xml

3. Add a CI job that regenerates tokens on every PR touching tokens.json
   - Run style-dictionary build, commit the output, fail if output changed without a source change
   - This prevents hand-editing generated files

4. Add a token linter that enforces naming and value constraints
   - All colors must be in the brand palette; no one-off hex values
   - Font sizes must be in the type scale; no 13px, 15px, etc.

5. Version the tokens: breaking changes (renaming, removing) require a major bump
   - Consumers can pin to a version during migration

Example

// style-dictionary.config.ts
import StyleDictionary from "style-dictionary";

StyleDictionary.extend({
  tokens: ["tokens.json"],
  platforms: {
    css: { transformGroup: "css", buildPath: "src/styles/", files: [{ destination: "tokens.css", format: "css/variables" }] },
    tailwind: { transformGroup: "js", buildPath: "src/tokens/", files: [{ destination: "tailwind.js", format: "javascript/module" }] },
    ios: { transformGroup: "ios-swift", buildPath: "ios/", files: [{ destination: "Tokens.swift", format: "ios-swift/class.swift" }] },
  },
}).buildAllPlatforms();

// CI: .github/workflows/tokens.yml
- run: npx style-dictionary build
- run: git diff --exit-code src/styles/tokens.css  # fail if hand-edited

Common Problems

  • ⚠Naming tokens by their value (color.blue500) instead of their role (color.surface.primary), so a rebrand requires renaming every token.
  • ⚠Hand-editing the generated CSS instead of the source JSON, so the next pipeline run overwrites the fix.
  • ⚠Not versioning tokens, so a rename breaks every consumer simultaneously with no migration path.
  • ⚠Putting non-design values (z-index, animation duration) in the token system, which becomes a dumping ground.

Best Practices

  • ✓Use a 3-tier token system: primitive (blue-500) → semantic (color.primary) → component (button.bg) — each layer can be remapped without touching the others.
  • ✓Generate a TypeScript type from the tokens so a typo in a token name is a compile error.
  • ✓Keep the token JSON in its own package (monorepo) so multiple apps consume the same version.
  • ✓Run the token linter as a pre-commit hook, not just CI — catch naming violations before they're pushed.