Convert Figma Variables to CSS Custom Properties
Export Figma variables and transform them into production-ready CSS custom properties your engineers can use immediately.
What you will need
Colors, spacing, typography. Variables require any paid Figma plan.
VS Code recommended. TextEdit or Notepad will also work for the small CSS file you produce.
Required only if you want to automate the JSON-to-CSS transform with the script in step 3.
Time to audit, export, transform, and document the result for engineering.
A designer on my team changed the primary blue in Figma on a Monday. The engineer changed it in CSS on a Thursday. For three days, the product looked different in Figma than in production, and nobody noticed until the QA report came in with 12 “color mismatch” tickets. All because there was no connection between the Figma variable and the CSS variable. They were two separate sources of truth pretending to be one.
This workflow bridges that gap. You will export your Figma variables, transform them into CSS custom properties, and establish a process where changes in Figma flow directly to code. No more copy-pasting hex values. No more “did the engineer update the color yet?” conversations.
Step 1: Audit Your Figma Variables
Before exporting anything, make sure your variables are clean. Open your Figma file, go to the Variables panel (click the diamond icon in the right sidebar), and review:
Check for these issues:
-
Naming consistency. Are all variables using the same format? Pick one:
color/background/primary(slashes),color.background.primary(dots), orcolor-background-primary(kebab). CSS custom properties use dashes, so kebab-case maps most cleanly. -
Proper aliasing. Semantic variables should reference primitives, not hardcoded values. If
background/primaryis set to#F3F4F6instead of aliasinggray/100, fix it now. Aliases preserve the connection between primitive and semantic layers. -
Mode coverage. If you have Light and Dark modes, verify every semantic variable has a value in both modes. Missing values in Dark mode will create broken themes.
Document what you find:
Total variables: 89
Colors: 52 (24 primitives, 28 semantic)
Spacing: 18
Border radius: 8
Typography: 11
Issues found:
- 4 semantic colors with hardcoded hex (should alias primitives)
- 2 variables missing Dark mode values
- 3 variables using slash notation, rest using kebab-case
Why this step matters
Exporting messy variables gives you messy CSS. Every inconsistency in Figma becomes an inconsistency in code. Spending 10 minutes cleaning up now saves hours of debugging later. Think of this as proofreading before publishing.
Step 2: Export Variables from Figma
Figma does not have a built-in “export variables to CSS” button (yet). You have three options:
Option A: Use a Figma plugin (easiest)
Install the “Variables to CSS” or “Design Tokens” plugin from the Figma Community:
- Go to Resources (Shift+I) in Figma
- Search for “Variables Export” or “Design Tokens”
- Run the plugin on your file
- Export as JSON or CSS
Option B: Use the Figma REST API (most control)
If you have a Figma API token, you can pull variables programmatically:
curl -s -H "X-Figma-Token: YOUR_TOKEN" \
"https://api.figma.com/v1/files/YOUR_FILE_KEY/variables/local" \
-o figma-variables.json
This gives you the raw JSON with all variable collections, modes, and values.
Option C: Manual export (smallest systems)
For small variable sets (under 50), you can copy values directly from the Figma Variables panel into a JSON file. Not glamorous, but it works.
Why this step matters
The method you choose depends on your scale. A 20-variable system? Manual copy is fine. A 200-variable system with 3 modes? You need the API. Pick the approach that matches your current size, not the size you hope to be.
Step 3: Transform Variables to CSS Custom Properties
Take your exported JSON and convert it to CSS. Here is a simple Node.js script that handles the transformation:
// transform-tokens.js
const fs = require('fs');
const tokens = JSON.parse(fs.readFileSync('figma-variables.json', 'utf8'));
function toCSSName(figmaName) {
return '--' + figmaName
.replace(/\//g, '-')
.replace(/\./g, '-')
.replace(/\s+/g, '-')
.toLowerCase();
}
function resolveValue(value) {
if (typeof value === 'object' && value.r !== undefined) {
const r = Math.round(value.r * 255);
const g = Math.round(value.g * 255);
const b = Math.round(value.b * 255);
const a = value.a !== undefined ? value.a : 1;
if (a < 1) {
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}
if (typeof value === 'number') {
return `${value}px`;
}
return value;
}
let css = ':root {\n';
for (const [name, value] of Object.entries(tokens)) {
css += ` ${toCSSName(name)}: ${resolveValue(value)};\n`;
}
css += '}\n';
fs.writeFileSync('variables.css', css);
console.log('Generated variables.css');
Run it:
node transform-tokens.js
The output will look like:
:root {
--color-primary: #3B82F6;
--color-background-primary: #F3F4F6;
--color-background-inverse: #111827;
--color-text-primary: #111827;
--color-text-secondary: #6B7280;
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--border-radius-sm: 4px;
--border-radius-md: 8px;
--border-radius-lg: 12px;
}
Why this step matters
The transformation script is where Figma’s naming becomes CSS’s naming. The toCSSName function normalizes slashes, dots, and spaces into CSS-friendly dashes. This is also where you decide on your CSS naming convention. Once set, every future export follows the same rules automatically.
Step 4: Handle Multiple Modes (Light/Dark)
If your Figma variables have Light and Dark modes, generate separate CSS blocks:
/* Light mode (default) */
:root {
--color-background-primary: #FFFFFF;
--color-background-secondary: #F3F4F6;
--color-text-primary: #111827;
--color-text-secondary: #6B7280;
--color-border-default: #E5E7EB;
}
/* Dark mode */
[data-theme="dark"] {
--color-background-primary: #111827;
--color-background-secondary: #1F2937;
--color-text-primary: #F9FAFB;
--color-text-secondary: #9CA3AF;
--color-border-default: #374151;
}
To generate this from a multi-mode export, update your script to group values by mode:
// For each mode, generate a separate CSS block
const modes = {
light: { selector: ':root', values: {} },
dark: { selector: '[data-theme="dark"]', values: {} }
};
// Parse your exported JSON and populate each mode's values
// Then generate CSS for each mode
for (const [modeName, mode] of Object.entries(modes)) {
css += `/* ${modeName} mode */\n`;
css += `${mode.selector} {\n`;
for (const [name, value] of Object.entries(mode.values)) {
css += ` ${toCSSName(name)}: ${resolveValue(value)};\n`;
}
css += '}\n\n';
}
Test it by toggling data-theme="dark" on your HTML element and verifying that colors switch correctly.
Why this step matters
CSS custom properties with data attributes is the most widely supported theming approach on the web. When your engineer adds data-theme="dark" to the <html> tag, every component that uses your variables automatically switches to dark mode. No class changes, no JavaScript per component. One attribute, everything updates.
Step 5: Deliver to Engineering
Package your CSS file and share it with engineering. Include a brief README that covers:
## Design Tokens (CSS Custom Properties)
### Files
- `variables.css` - All design tokens as CSS custom properties
### Usage
Import in your global CSS:
@import './variables.css';
Use in any component:
.button {
background-color: var(--color-primary);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius-md);
}
### Theming
Add data-theme="dark" to the html element to activate dark mode.
All components using CSS variables will update automatically.
### Updates
These variables are generated from Figma. When design tokens change:
1. Designer exports updated variables from Figma
2. Run: node transform-tokens.js
3. Commit the updated variables.css
For ongoing updates, consider automating this with a GitHub Action that runs the transform script on every push to the tokens repository.
Why this step matters
The handoff is not just the CSS file. It is the contract between design and engineering. The README tells engineers exactly how to use the tokens and exactly what happens when they change. Without this, engineers will hardcode values “just to be safe” and your tokens become decorative.
What You Get
After completing this workflow, you have:
- Clean, audited Figma variables ready for export
- A transformation script that converts Figma JSON to CSS custom properties
- Light and dark mode support through data attributes
- A delivery package with documentation for engineering
- A repeatable process for future token updates
Common Pitfalls
- Exporting primitives and semantics as a flat list. Keep your CSS organized. Primitives should be in a separate file or section from semantic tokens. Engineers need to know which variables are safe to use directly and which are internal.
- Forgetting about fallback values. CSS custom properties with
var()can include fallbacks:color: var(--color-primary, #3B82F6). Discuss with your engineering team whether they want fallbacks in the codebase. - Not versioning your tokens. When you change a token value, consumers need to know. Add a version number or changelog to your token package so teams can update deliberately.
Export your Figma variables to CSS and ship them to engineering
-
Audit and export one real variable collection
Open a Figma file you own. Open the Variables panel. Pick one collection (colors or spacing). Walk the naming, aliasing, and mode coverage checklist from Step 1. Fix anything broken. Then export using either a plugin or the REST API command in Step 2. Save the result as
figma-variables.jsonin a new folder.- Every semantic variable aliases a primitive, never a raw hex
- Naming is consistent across the collection (one of dots, slashes, or kebab)
- The JSON file exists on disk and opens without syntax errors
-
Transform to CSS and deliver to engineering with a README
Save the transform script from Step 3 as
transform-tokens.js. Runnode transform-tokens.js. Open the generatedvariables.cssand verify the output matches your audit. If you support Light and Dark modes, extend the script using the pattern in Step 4. Packagevariables.cssplus the README from Step 5 and share the folder with engineering.variables.csscontains CSS custom properties under:rootwith a dark mode block if applicable- An engineer can import the file and use
var(--color-background-primary)without asking you what it means - The README tells them exactly what to do when tokens change (rerun the script, commit the diff)
Finished this lesson?
Mark it complete to track your progress through "Agentic Design Systems".
The guides alone saved me a full day of work every sprint.
- All guides, prompts, and templates
- Starter kits and templates
- New content every week
- Priority support