EmpireUI
Get Pro
← Blog7 min read#design-tokens#w3c#design-systems

Design Tokens W3C Standard: Implementing the Spec in 2026

The W3C Design Tokens spec is finally stable. Here's how to implement it in your React + Tailwind stack in 2026 without breaking everything you've already built.

Abstract geometric shapes with color swatches representing design token systems

The W3C Design Tokens Spec Is Stable — Now What?

Honestly, most teams ignored design tokens for years because the tooling was a mess and every design system did it differently. Figma had its own format. Style Dictionary had its own. Amazon had theirs. Nobody talked to each other.

That changed. The W3C Design Tokens Community Group shipped a stable spec — formally called the Design Token Format Module — and tool authors have been aligning behind it fast. If you're starting a new project in late 2026, this is the format you want. If you're maintaining an existing one, you'll need to migrate eventually.

This isn't theoretical territory anymore. Style Dictionary v4, Theo, and Token Transformer all consume the W3C format. Figma Variables can export close-enough JSON with a plugin. The ecosystem reached a tipping point somewhere around mid-2025 and it's not going back.

What the W3C Format Actually Looks Like

The spec defines tokens as JSON objects with a $value field, a $type field, and optional $description and $extensions. Groups are just nested objects. That's it. Deliberately simple.

Here's a real tokens file following the spec. Notice the $type annotation at the group level — child tokens inherit it unless they override. This is one of the spec's most useful features for keeping files manageable.

{
  "color": {
    "$type": "color",
    "brand": {
      "primary": { "$value": "#6366f1", "$description": "Indigo-500, main brand color" },
      "secondary": { "$value": "#8b5cf6" }
    },
    "surface": {
      "base": { "$value": "rgba(255,255,255,0.15)", "$description": "Glassmorphism card base" },
      "elevated": { "$value": "rgba(255,255,255,0.22)" }
    }
  },
  "spacing": {
    "$type": "dimension",
    "xs": { "$value": "4px" },
    "sm": { "$value": "8px" },
    "md": { "$value": "16px" },
    "lg": { "$value": "24px" },
    "xl": { "$value": "40px" }
  }
}

The $type values the spec defines are: color, dimension, fontFamily, fontWeight, duration, cubicBezier, number, strokeStyle, border, transition, shadow, gradient, and typography. If you need something outside that list, $extensions is your escape hatch — namespace it with a reverse domain like com.yourteam.customType.

Token References and Alias Chains

References are written in curly-brace notation: {color.brand.primary}. This is how you build a semantic layer on top of a raw palette. Semantic tokens like action.primary.background point at palette tokens like color.brand.primary. Your components consume the semantic tokens only — never the palette directly.

Why does this matter? Because when your brand color shifts from #6366f1 to #4f46e5, you change it in one place. Every component that consumes {action.primary.background} just works. No grep-and-replace. No missed instance in some forgotten modal.

Alias chains can go multiple levels deep but keep it to two: palette → semantic → (optionally) component. Three levels is usually fine. Four levels is where people start getting confused about what's actually resolving to what during a token pipeline build.

Building a Transform Pipeline with Style Dictionary v4

Style Dictionary v4 ships with native W3C format support — you don't need a custom parser anymore. Point it at your tokens file, configure your platforms, and it generates CSS custom properties, Tailwind config, JS constants, or whatever else you need.

Here's a minimal sd.config.js that reads a W3C tokens file and outputs CSS variables plus a Tailwind v4.0.2 theme extension. The expandCompositeTokens option tells SD to flatten composite types like shadow and typography into individual properties.

// sd.config.js
import StyleDictionary from 'style-dictionary';

export default {
  source: ['tokens/**/*.json'],
  preprocessors: ['tokens-studio'], // if you're coming from Figma via Tokens Studio
  platforms: {
    css: {
      transformGroup: 'css',
      expand: {
        composition: true,
        typography: true,
        border: true,
        shadow: true,
      },
      prefix: 'empire',
      buildPath: 'apps/web/src/styles/',
      files: [
        {
          destination: 'tokens.css',
          format: 'css/variables',
          options: { outputReferences: true },
        },
      ],
    },
    js: {
      transformGroup: 'js',
      buildPath: 'apps/web/src/tokens/',
      files: [
        {
          destination: 'index.ts',
          format: 'javascript/es6',
        },
      ],
    },
  },
};

Run style-dictionary build and you get --empire-color-brand-primary: #6366f1 in your CSS file and a typed TS export in your JS file. Wire the CSS file into your global layout and you're done. The spacing system you build on top of these tokens will stay consistent across every component.

Connecting Tokens to Tailwind v4

Tailwind v4's CSS-first configuration means connecting your design tokens is simpler than it was in v3. You define theme values directly in CSS using @theme, which means your generated token file and your Tailwind theme can be the same file — or at least reference each other cleanly.

/* apps/web/src/styles/tailwind-theme.css */
@import 'tokens.css'; /* brings in --empire-* custom properties */

@theme {
  --color-brand-primary: var(--empire-color-brand-primary);
  --color-brand-secondary: var(--empire-color-brand-secondary);
  --color-surface-base: var(--empire-color-surface-base);

  --spacing-xs: var(--empire-spacing-xs);   /* 4px */
  --spacing-sm: var(--empire-spacing-sm);   /* 8px */
  --spacing-md: var(--empire-spacing-md);   /* 16px */
  --spacing-lg: var(--empire-spacing-lg);   /* 24px */

  --font-sans: var(--empire-font-family-sans);
  --font-mono: var(--empire-font-family-mono);
}

Now bg-brand-primary and p-md work as Tailwind utility classes, and they're backed by W3C tokens. When you update a token value, Tailwind picks it up automatically on the next build. No Tailwind config file to touch. This approach plays nicely with the theme toggle pattern — swap a data-theme attribute on :root and your tokens resolve to different values instantly.

One gotcha: Tailwind v4 doesn't automatically pick up every CSS custom property as a utility. Only properties explicitly declared inside @theme become utilities. So you'll want to be deliberate about which tokens get promoted to the theme versus staying as raw CSS variables consumed directly in component stylesheets.

Multi-Theme Tokens: Light, Dark, and Brand Variants

The W3C spec doesn't define a native mechanism for themes — it's scoped to token format, not token switching. You handle theming in the output layer. The two standard approaches are media-query-based (using prefers-color-scheme) and attribute-based (using [data-theme='dark']).

Attribute-based gives you more control — users can override their OS setting, and you can support brand themes beyond just light/dark. Define your semantic tokens once, then override their values per theme. Here's what that looks like after Style Dictionary transforms your tokens:

:root {
  --empire-color-surface-base: rgba(255,255,255,0.15);
  --empire-color-text-primary: #111827;
  --empire-color-text-secondary: #6b7280;
}

[data-theme='dark'] {
  --empire-color-surface-base: rgba(0,0,0,0.25);
  --empire-color-text-primary: #f9fafb;
  --empire-color-text-secondary: #9ca3af;
}

[data-theme='violet'] {
  --empire-color-brand-primary: #7c3aed;
  --empire-color-brand-secondary: #a78bfa;
}

Keep your palette tokens constant across themes — only semantic tokens change. That rule makes theming predictable. If you want a deeper look at how we structure color decisions, the color system guide covers the palette → semantic → component hierarchy in more detail.

Syncing Figma Variables to W3C Tokens

Figma Variables are close to the W3C format but not identical. The mapping works like this: Variable Collections become token groups, Variable Modes become themes, and Value Types map directly to $type. The gap is mainly naming conventions and some composite types Figma doesn't support natively.

Tokens Studio for Figma (free tier covers most teams) exports directly to W3C-compatible JSON. If you're going fully manual, the Figma REST API's /variables endpoint returns enough data to write your own transform. That's worth doing once if you need zero plugin dependencies.

Is the Figma-to-code sync ever truly automatic? Not yet. There's always a human-in-the-loop step where someone decides which variable changes are intentional and which are accidents. The Figma to React workflow article covers that handoff in detail — the token pipeline makes it mechanical once the decisions are made, but the decisions still need making.

Validating Tokens and Avoiding Drift

Token drift happens when your source tokens and your shipped CSS get out of sync. It's subtle and annoying. A component gets hardcoded to #6366f1 instead of var(--empire-color-brand-primary) and nobody notices until the rebrand.

Add a lint step. Both ESLint (via eslint-plugin-no-hardcoded-colors or similar) and Stylelint can flag raw hex values in component files. Wire this into your CI pipeline alongside your type-check. If # appears in a .tsx or .css component file, the build fails. Harsh but effective.

For token validity itself, use the W3C token validator (@tokens-studio/sd-transforms ships one) as a pre-build step. It catches type mismatches, broken references, and malformed values before Style Dictionary tries to transform them. Catching a broken {color.brand.typo} reference in CI beats finding it in a production style regression.

The Storybook setup is also useful here — running a visual diff against a token-aware Storybook story catches unintentional rendering changes when tokens update. Cheap insurance.

FAQ

Is the W3C Design Tokens spec finalized or still a draft?

As of late 2026, the Design Token Format Module is in Candidate Recommendation status — stable enough that major tools have shipped production support for it. The core format (token types, references, groups) won't change. Some composite types and the $extensions conventions are still being refined by the community group.

Can I use the W3C token format with Tailwind v4 without Style Dictionary?

Yes, but you'll need to write your own transform step. The W3C format outputs JSON; Tailwind v4 needs CSS @theme declarations. That transform is maybe 50 lines of JavaScript if your token structure is straightforward. Style Dictionary v4 just saves you writing and maintaining that code yourself.

What's the difference between $type: color and $type: dimension?

The $type field tells consuming tools how to interpret and transform the $value. A color value like #6366f1 might get converted to HSL or RGBA depending on your platform config. A dimension value like 8px or 0.5rem might get converted to px integers for a native mobile platform. Without $type, tools have to guess — and they don't always guess right.

How do I handle tokens for elevation shadows across themes?

Use the shadow composite type. Define your shadow tokens with color, offsetX, offsetY, blur, and spread sub-properties. Then in your CSS output, each theme overrides the shadow color value using a semantic alias. For example, {color.shadow.elevation-1} resolves to rgba(0,0,0,0.12) in light mode and rgba(0,0,0,0.45) in dark mode.

Do design tokens work with CSS Modules or only global CSS?

They work fine with CSS Modules. Your token file outputs CSS custom properties to a global stylesheet — CSS custom properties cascade, so they're accessible inside any scoped CSS Module file. You reference them the same way: background: var(--empire-color-surface-base). No special setup needed.

What happens when Figma Variables and my token file get out of sync?

Components get built against stale token values and visual inconsistencies creep in. The fix is to treat your W3C token file as the single source of truth and generate Figma Variables from it — not the other way around. Tokens Studio supports bidirectional sync, but unidirectional (code → design) is easier to reason about for engineering-led teams.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Figma Design Tokens Sync: Automatic Export to CSS VariablesDark Mode in a Design System: Semantic Tokens That WorkTailwind + CSS Variables: Dynamic Theming Without JavaScriptCustomizing shadcn/ui: Colors, Radius, and Dark Mode Tokens