Skip to main content

Design Tokens: Building a Token System That Works with Tailwind and Figma

June 2, 2026

</>

Design tokens are the atomic values that make up a design system: colors, spacing, typography scales, border radii, shadows, and animation durations. When defined properly, a token like --color-brand is the single source of truth for your primary brand color — change it once and every button, link, and accent across your entire application updates.

Without design tokens, you get color drift: the designer uses #6366F1 in Figma, the developer hardcodes #6063ef in the CSS (typo), and six months later nobody knows which is correct.

This post shows you how to build a token system that is synchronized between Figma and your codebase, using the W3C Design Token format and Tailwind CSS v4's native @theme support.


The W3C Design Token Format

The W3C Community Group has standardized a JSON format for design tokens that tools like Figma, Style Dictionary, and Tokens Studio can read and write:

{
  "color": {
    "brand": {
      "$value": "#6366f1",
      "$type": "color",
      "$description": "Primary brand color. Used for CTAs and active states."
    },
    "brand-hover": {
      "$value": "#4f46e5",
      "$type": "color"
    },
    "text-primary": {
      "$value": "{color.neutral.900}",
      "$type": "color"
    }
  },
  "spacing": {
    "xs": { "$value": "0.25rem", "$type": "dimension" },
    "sm": { "$value": "0.5rem",  "$type": "dimension" },
    "md": { "$value": "1rem",    "$type": "dimension" },
    "lg": { "$value": "1.5rem",  "$type": "dimension" },
    "xl": { "$value": "2rem",    "$type": "dimension" },
    "2xl": { "$value": "3rem",   "$type": "dimension" }
  },
  "borderRadius": {
    "sm": { "$value": "0.25rem", "$type": "dimension" },
    "md": { "$value": "0.375rem", "$type": "dimension" },
    "lg": { "$value": "0.5rem",  "$type": "dimension" },
    "full": { "$value": "9999px", "$type": "dimension" }
  },
  "typography": {
    "fontFamily": {
      "sans": { "$value": "'Outfit', ui-sans-serif, system-ui", "$type": "fontFamily" },
      "mono": { "$value": "'JetBrains Mono', ui-monospace", "$type": "fontFamily" }
    },
    "fontSize": {
      "sm":   { "$value": "0.875rem", "$type": "dimension" },
      "base": { "$value": "1rem",     "$type": "dimension" },
      "lg":   { "$value": "1.125rem", "$type": "dimension" },
      "xl":   { "$value": "1.25rem",  "$type": "dimension" },
      "2xl":  { "$value": "1.5rem",   "$type": "dimension" },
      "3xl":  { "$value": "1.875rem", "$type": "dimension" },
      "4xl":  { "$value": "2.25rem",  "$type": "dimension" }
    }
  },
  "shadow": {
    "sm": { "$value": "0 1px 2px rgba(0,0,0,0.05)", "$type": "shadow" },
    "md": { "$value": "0 4px 6px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.06)", "$type": "shadow" },
    "lg": { "$value": "0 10px 15px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05)", "$type": "shadow" }
  }
}

Store this as tokens/tokens.json in your repository.


Syncing Tokens from Figma

Tokens Studio for Figma is a Figma plugin that lets designers edit tokens in a structured panel and sync them directly to your GitHub repository:

  1. Install Tokens Studio in Figma.
  2. Connect it to your GitHub repository.
  3. Configure the sync path to tokens/tokens.json.
  4. Designers update tokens in Figma → Tokens Studio creates a PR with the updated JSON.

Option 2: Figma Variables API (Native Figma)

Figma's native Variables feature (available in Professional plans) stores design tokens as Figma Variables that can be exported via the REST API:

// scripts/sync-figma-tokens.ts
async function fetchFigmaVariables(fileId: string) {
  const response = await fetch(
    `https://api.figma.com/v1/files/${fileId}/variables/local`,
    { headers: { 'X-Figma-Token': process.env.FIGMA_TOKEN! } }
  );
  const data = await response.json();

  // Transform Figma variables to W3C token format
  const tokens: Record<string, object> = {};

  for (const [id, variable] of Object.entries(data.meta.variables as Record<string, { name: string; resolvedType: string; valuesByMode: Record<string, { r: number; g: number; b: number; a: number }> }>)) {
    const collection = data.meta.variableCollections[variable.name];
    const modeId = Object.keys(variable.valuesByMode)[0];
    const value = variable.valuesByMode[modeId];

    if (variable.resolvedType === 'COLOR') {
      const hex = rgbToHex(value.r, value.g, value.b);
      tokens[variable.name] = { $value: hex, $type: 'color' };
    }
  }

  return tokens;
}

function rgbToHex(r: number, g: number, b: number): string {
  const toHex = (n: number) => Math.round(n * 255).toString(16).padStart(2, '0');
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}

Transforming Tokens to Tailwind CSS v4

Use Style Dictionary to transform the W3C JSON format into Tailwind-compatible CSS variables:

pnpm add -D style-dictionary
// style-dictionary.config.js
import StyleDictionary from 'style-dictionary';

const sd = new StyleDictionary({
  source: ['tokens/tokens.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      prefix: 'ds',
      files: [
        {
          destination: 'app/tokens.css',
          format: 'css/variables',
          options: {
            selector: ':root',
            outputReferences: true,
          },
        },
      ],
    },
  },
});

await sd.buildAllPlatforms();

This generates app/tokens.css:

:root {
  --ds-color-brand: #6366f1;
  --ds-color-brand-hover: #4f46e5;
  --ds-spacing-xs: 0.25rem;
  --ds-spacing-sm: 0.5rem;
  --ds-spacing-md: 1rem;
  --ds-border-radius-md: 0.375rem;
  --ds-font-family-sans: 'Outfit', ui-sans-serif, system-ui;
}

Consuming Tokens in Tailwind CSS v4

Import the generated token CSS and map tokens to Tailwind's @theme:

/* app/global.css */
@import "tailwindcss";
@import "./tokens.css";  /* Import generated design tokens */

@theme {
  /* Map design tokens to Tailwind utilities */
  --color-brand:       var(--ds-color-brand);
  --color-brand-hover: var(--ds-color-brand-hover);

  --spacing-xs: var(--ds-spacing-xs);
  --spacing-sm: var(--ds-spacing-sm);
  --spacing-md: var(--ds-spacing-md);

  --radius-md: var(--ds-border-radius-md);

  --font-sans: var(--ds-font-family-sans);
}

Now you can use bg-brand, text-brand, p-md, rounded-md as Tailwind utility classes — all driven by the design tokens.


Dark Mode Tokens

// tokens/dark.json — dark mode overrides
{
  "color": {
    "brand": { "$value": "#818cf8", "$type": "color" },
    "background": { "$value": "#0f172a", "$type": "color" },
    "text-primary": { "$value": "#f1f5f9", "$type": "color" }
  }
}
/* Generated dark mode tokens */
[data-theme="dark"] {
  --ds-color-brand: #818cf8;
  --ds-color-background: #0f172a;
  --ds-color-text-primary: #f1f5f9;
}

Token Sync CI Pipeline

Automate token synchronization on every merge:

# .github/workflows/token-sync.yml
name: Design Token Sync

on:
  push:
    paths: ['tokens/**']
    branches: [main]

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '22' }
      - run: npm ci

      - name: Transform tokens
        run: node style-dictionary.config.js

      - name: Commit generated CSS
        run: |
          git config user.name "Token Bot"
          git config user.email "tokens@sabaoon.dev"
          git add app/tokens.css
          git diff --staged --quiet || git commit -m "chore: sync design tokens"
          git push

Conclusion

Design tokens are the foundation of a scalable design system. By storing tokens in the W3C standard JSON format, syncing them from Figma via Tokens Studio or the Variables API, and transforming them into Tailwind CSS v4 @theme variables with Style Dictionary, you create a single source of truth that keeps designers and developers in sync. When a designer updates the brand color in Figma, a PR is created, a CI job transforms the token, and every element across the entire application reflects the change automatically — with zero manual CSS hunting.

Recommended Posts