Design systems break down at the handoff boundary. A designer updates the primary brand color in Figma, the developer hasn't heard yet, and two weeks later the button on the checkout page is a slightly different shade of blue. This isn't a communication problem — it's a tooling problem.
Figma Variables and Tailwind CSS design tokens are two halves of the same idea. When you map them correctly, a single source of truth lives in your Figma file and flows automatically into your codebase. This guide shows you how to build that pipeline.
What are Figma Variables?
Figma Variables are named, reusable values — colors, numbers, strings, and booleans — that can be applied to any property in a Figma design and organized into collections and modes. Introduced in 2023 and expanded through 2024–25, Variables replaced the older Styles system for most token use cases. Where Styles were a flat list of presets, Variables support:
- Collections — groups of related tokens (e.g., "Color/Brand", "Spacing/Scale")
- Modes — variants of the same collection (e.g., Light, Dark; Desktop, Mobile)
- Aliases — variables that reference other variables (semantic tokens pointing to primitive tokens)
- Variable types — Color, Number, String, Boolean
A well-structured variable collection in Figma is already a design token system. The job now is to extract it into code.
How design tokens map to Tailwind
Tailwind CSS is configured via a tailwind.config file where you extend (or replace) the default theme. The theme.extend object accepts keys for every design concern Tailwind manages: colors, spacing, borderRadius, fontFamily, fontSize, boxShadow, and more.
Design tokens map to Tailwind keys like this:
| Figma Variable collection | Tailwind theme key |
|---|---|
| Color / Primitive | theme.extend.colors |
Color / Semantic (e.g., color/text/primary) | Nested under colors |
| Spacing / Scale | theme.extend.spacing |
| Border Radius | theme.extend.borderRadius |
| Typography / Font Family | theme.extend.fontFamily |
| Typography / Font Size | theme.extend.fontSize |
| Shadow | theme.extend.boxShadow |
The naming convention matters: Tailwind utility classes are derived directly from these keys. A token named brand.500 becomes the class bg-brand-500. Keep names semantic and predictable.
Step 1 — Structure your Figma Variables correctly
Before you can export anything useful, your Figma file needs a clean variable structure. Use two layers:
Primitive tokens — raw values with no semantic meaning:
color/palette/blue/50 → #EFF6FF
color/palette/blue/500 → #3B82F6
color/palette/blue/900 → #1E3A5FSemantic tokens — aliases that point to primitives and carry intent:
color/brand/primary → color/palette/blue/500
color/text/default → color/palette/gray/900
color/background/page → color/palette/whiteThis two-layer model means you can retheme the entire product by changing only the semantic-to-primitive mappings — without touching any component.
Step 2 — Export variables from Figma
There is no official native "export to JSON" button in Figma for Variables yet (as of mid-2026), but three approaches work reliably:
- Figma Tokens / Token Studio plugin — the industry standard for bidirectional token sync. Exports W3C-compliant design tokens JSON.
- Variables Import & Export plugin — lighter-weight, exports to a flat JSON format.
- Figma REST API —
GET /v1/files/:file_key/variables/localreturns all variable collections programmatically. Best for automated pipelines.
For a professional pipeline, use the REST API or Token Studio — both integrate cleanly with build scripts.
Step 3 — Transform tokens with Style Dictionary
Style Dictionary by Amazon is the standard tool for transforming design token JSON into platform-specific output (CSS variables, Tailwind config, iOS Swift, Android XML, and more).
A minimal Style Dictionary config targeting Tailwind looks like this:
// sd.config.js
module.exports = {
source: ['tokens/**/*.json'],
platforms: {
tailwind: {
transformGroup: 'js',
buildPath: 'src/styles/',
files: [
{
destination: 'tokens.js',
format: 'javascript/module',
},
],
},
},
}Run style-dictionary build and you get a structured JS object ready to drop into tailwind.config.
Step 4 — Wire the tokens into tailwind.config
Here's a realistic example of what the generated Tailwind theme config looks like after running your token pipeline:
// tailwind.config.js
const tokens = require('./src/styles/tokens')
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {
colors: {
brand: {
50: tokens.color.palette.blue[50].value,
100: tokens.color.palette.blue[100].value,
500: tokens.color.palette.blue[500].value,
900: tokens.color.palette.blue[900].value,
},
text: {
default: tokens.color.text.default.value,
muted: tokens.color.text.muted.value,
inverse: tokens.color.text.inverse.value,
},
bg: {
page: tokens.color.background.page.value,
surface: tokens.color.background.surface.value,
overlay: tokens.color.background.overlay.value,
},
},
spacing: {
xs: tokens.spacing.xs.value, // 4px
sm: tokens.spacing.sm.value, // 8px
md: tokens.spacing.md.value, // 16px
lg: tokens.spacing.lg.value, // 24px
xl: tokens.spacing.xl.value, // 32px
'2xl': tokens.spacing['2xl'].value, // 48px
},
borderRadius: {
sm: tokens.radius.sm.value, // 4px
md: tokens.radius.md.value, // 8px
lg: tokens.radius.lg.value, // 12px
full: tokens.radius.full.value, // 9999px
},
fontFamily: {
sans: [tokens.typography.fontFamily.sans.value, 'sans-serif'],
mono: [tokens.typography.fontFamily.mono.value, 'monospace'],
},
fontSize: {
sm: [tokens.typography.size.sm.value, { lineHeight: '1.5' }],
base: [tokens.typography.size.base.value, { lineHeight: '1.6' }],
lg: [tokens.typography.size.lg.value, { lineHeight: '1.4' }],
xl: [tokens.typography.size.xl.value, { lineHeight: '1.3' }],
'2xl': [tokens.typography.size['2xl'].value, { lineHeight: '1.2' }],
},
},
},
plugins: [],
}Avoid hardcoding hex values directly in tailwind.config. Always reference
the token object. If you paste raw values in, the pipeline breaks and the
design-code sync advantage disappears entirely.
Step 5 — Keeping design and code in sync
The pipeline is only valuable if it stays alive. Two patterns make that happen:
CI sync check — add a build step that re-exports Figma variables and runs Style Dictionary. If the generated output differs from what's committed, the build fails. This catches any case where a designer updates tokens in Figma but the code hasn't been updated.
Token versioning — treat your tokens JSON like an API. Version it, changelog it, and tag releases. When a semantic token changes name, it's a breaking change. When a primitive value shifts slightly, it's a patch.
What about Tailwind v4?
Tailwind CSS v4 (released 2025) shifts configuration from tailwind.config.js to a CSS-first @theme block in your main stylesheet. The token values are the same — you're just writing:
@theme {
--color-brand-500: #3B82F6;
--spacing-md: 1rem;
--radius-lg: 0.75rem;
}Style Dictionary supports CSS variable output natively, so the pipeline adapts cleanly to v4.
The bottom line
Figma Variables and Tailwind CSS tokens are a natural pair. When the mapping is systematic — primitives to semantics in Figma, semantics to Tailwind keys in code — you get a design system where one change propagates everywhere. The upfront cost is a few hours of pipeline setup. The ongoing benefit is a codebase that never drifts from the design.
If you want this kind of pixel-perfect, token-driven implementation built for you, Figmafy specializes in Figma to React projects that ship clean, maintainable component libraries from day one.
Daniel Cruz
Lead Front-End Engineer
Daniel leads front-end engineering at Figmafy, specializing in React, Next.js, and design-system development. He has converted hundreds of Figma files into clean, accessible, high-performance code.