How to Create and Export Design Tokens in Figma for Scalable UI - Pipeline Details
Automate - Extract - Distribute

Building on my previous post, this article provides an in-depth exploration of the token extraction pipeline architecture. I'll also compare this approach with Token Studio, a popular Figma plugin for design token management.
The pipeline is central to the extraction , transformation and bundling of the Figma design tokens. There are two major aspects:
Figma API Integration
The codebase connects to Figma’s API, authenticates, and fetches design tokens directly from the source files. This enables automated, up-to-date extraction without manual exports. All we need is a Figma personal access token and a dedicated FigmaAPI class that handles authentication and communication with Figma's REST endpoints. Check the documentation on the Figma API’s here
Sample code snippet:
export default class FigmaApi {
private baseUrl = 'https://api.figma.com'
private token: string
constructor(token: string) {
this.token = token
}
async getAllFigmaVariables(fileKey: string) {
const resp = await axios.request<GetFigmaVariablesResponse>({
url: `\({this.baseUrl}/v1/files/\){fileKey}/variables/local`,
headers: {
Accept: '*/*',
'X-Figma-Token': this.token,
},
})
return resp.data
}
}
Key considerations:
Authentication: Personal access tokens are stored securely in environment variables
File identification: Each Figma file has a unique FILE_KEY that identifies the design system source
API versioning: The v1 endpoint ensures stability across Figma's evolving API surface
Token Transformation: From Figma to CSS
The transformation pipeline is the heart of the system, converting Figma's REST API responses into production-ready CSS variables. This multi-stage process handles aliases, multi-mode tokens, color conversions, and special character sanitization. One of the most important and a useful library used here is Style Dictionary. It offers the following benefits
Custom Format Extensibility - The custom formatter demonstrates Style Dictionary's most powerful feature—complete control over output format while leveraging its core token infrastructure. It generates mode-specific CSS with alias preservation—impossible with default formatters
Focus on output format logic (mode blocks, alias handling)
Free multi-platform support (add Android/iOS builds easily)
Industry-standard token format (compatible with Design Tokens Community Group spec)
Stage 1 - Figma Variables to JSON Tokens. The first transformation stage converts Figma's variable format into Style Dictionary-compatible JSON after fetching the variables from Figma.
async function main() {
const fileKey = process.env.FILE_KEY
const api = new FigmaApi(process.env.PERSONAL_ACCESS_TOKEN)
// Fetch all figma variables from Figma
const figmaVariables = await api.getAllFigmaVariables(fileKey)
// Transform to token files grouped by collection
const tokensFiles = tokenFilesFromFigmaVariables(figmaVariables)
// Write JSON files to disk inside the outputDir
let outputDir = 'tokens_new'
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir)
}
// More code
}
Depending on the structure of the JSON, it is not a straight forward conversion to a compatible JSON. Challenges usually encountered include:
Special characters in names: Figma allows spaces, dots, and hyphens in variable names. These must be sanitized for file system compatibility and normalized for CSS variable naming (spaces become hyphens, dots become delimiters)
Mode handling: Collections with multiple modes (light/dark) require special grouping logic to separate tokens by mode while maintaining a hierarchical structure
Alias preservation: References between variables must be captured as aliases (e.g., {Global.white.primary}) rather than resolved immediately, allowing downstream tools to decide when to resolve them
Type mapping: Figma's FLOAT type maps to number, BOOLEAN to boolean, etc., ensuring semantic correctness in the output
Custom method like tokenFilesFromFigmaVariables helps mitigating the challenges and converts Figma's variable format into Style Dictionary-compatible JSON
Sample code snippet:
function tokenFilesFromFigmaVariables(figmaVariables: LocalVariable[]): { [fileName: string]: any } {
const tokenFiles: { [fileName: string]: any } = {};
figmaVariables.forEach((variable) => {
const collection = /* find collection for this variable */;
const hasMultipleModes = collection.modes.length > 1;
collection.modes.forEach((mode) => {
// Sanitize collection name for filesystem
const collectionName = collection.name.replace(/[/\\?%*:|"<>]/g, '-');
const fileName = `${collectionName}.json`;
// Build hierarchical path
let path: string[];
if (hasMultipleModes) {
// Prefix with mode name for multi-mode collections
const modeName = mode.name.replace(/\s/g, '');
path = [modeName, ...variable.name.split('/')];
} else {
path = variable.name.split('/');
}
// Navigate/create nested structure
let parent: any = tokenFiles[fileName];
const tokenName = path.pop()!;
path.forEach((groupName) => {
parent[groupName] = parent[groupName] || {};
parent = parent[groupName];
});
// Create token with metadata
parent[tokenName] = {
type: figmaTokenType(variable),
value: figmaTokenValue(variable, mode.modeId, figmaVariables),
description: variable.description
}
});
});
return tokenFiles;
}
At the end of Stage 1, we should be able to find the newly extracted token files in the destination that has been specified
Stage 2 - Once the figma design tokens are extracted into the JSON files, we create a config for our StyleDictionary. In this we define the source files, multi-platform support and the custom formatter that generates mode-aware CSS with intelligent alias handling. The implementation will vary depending on the JSON structure and the kind of output desired. In my case it preserves semantic relationships by emitting original alias notation in mode blocks while resolving global primitives. The formatter generates five distinct sections in the output CSS:
Global Color Primitives (:root**) -** Emits resolved hex values for base color palette tokens that have no mode variation.
:root {
--global-white-primary: #ffffff;
--global-red-90: #ff000f;
--opacity-black-16: #00000029;
}
Mode-Specific Semantic Colors (.mode-light / .mode-dark**) -** Emits original alias notation ({...} braces) to preserve semantic references, later converted to var() by postprocessor. Includes nested elevation classes with resolved box-shadow values.
.mode-light {
--brand-white: {Global.white.primary};
--background-base: {Global.white.primary};
}
Spacing & Sizing Tokens (:root**) -** Spacing tokens emit resolved pixel values; MO size tokens emit original aliases to reference spacing scale.
:root {
--spacing-15: 48px;
--text-font-size-heading-1: {spacing-15};
}
Typography Utility Classes - Parses composite typography values and generates CSS utility classes with decomposed properties (font-family, size, weight, line-height).
.heading-display-h1 {
font-family: 'ABCvoice Display';
font-size: 48px;
line-height: 64px;
font-weight: 600;
}
Language Mode Overrides (.language-*) - Groups language-specific font families by locale, enabling i18n font switching via class selectors.
.language-jp {
--jp-abcvoice: 'ABCvoice JP';
--jp-abcvoice-display: 'ABCvoice Display';
}
Stage 3 - Postprocessor Normalization. In the final stage, the post processor performs normalisation. It does the following:
Alias conversion: {Global.white.primary} -> var(--global-white-primary)
Spacing normalization: {spacing-15} -> var(--spacing-15)
Brand token mapping: Map --brand-white to var(--global-white-primary)
Status deduplication: Remove duplicate declarations
Sample code snippet:
function aliasToVarName(alias) {
const m = String(alias).trim().match(/^\{([^}]+)\}$/);
if (!m) return null;
// Convert {Global.white.primary} → --global-white-primary
const pathStr = normalizePrefixes(m[1]).replace(/[.]/g, '-').toLowerCase();
return `--${pathStr}`;
}
function rewriteModeBlockResolveAliases(inner, rootNameToVal, statusRootVars) {
const declRe = /(\s*--([a-z0-9_-]+)\s*:\s*)([^;]+)(;)/gi;
return inner.replace(declRe, (full, pre, name, val, suf) => {
const valTrimmed = val.trim();
// Handle brace aliases: {spacing-15} → var(--spacing-15)
if (/^\{[^}]+\}$/.test(valTrimmed)) {
const varName = aliasToVarName(valTrimmed);
if (varName) {
return `\({pre}var(\){varName})${suf}`;
}
}
// Handle brand token mapping: --brand-white → var(--global-white-primary)
if (name === 'brand-white' && rootNameToVal.has('--global-white-primary')) {
return `\({pre}var(--global-white-primary)\){suf}`;
}
// Pass through literal values unchanged
return full;
});
}
The final output is a Production-Ready CSS. Mode tokens reference globals via var(), enabling runtime theme switching. Browsers cache var() resolutions efficiently. It also Aliases document semantic relationships (--bg-primary -> --global-white). The final CSS output demonstrates the multi-stage transformation:
/* Global colors tokens */
:root {
--global-white-primary: #ffffff;
--global-black-primary: #000000;
}
/* Semantic color tokens and elevation styles by mode */
.mode-light {
--brand-white: var(--global-white-primary);
--background-base: var(--global-white-primary);
.elevation-4 {
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.08), 0px 2px 4px 0px rgba(0, 0, 0, 0.08), 0px 8px 16px 0px rgba(0, 0, 0, 0.12);
}
}
/* Global spacing tokens */
:root {
--spacing-15: 48px;
}
/* size tokens */
:root {
--text-font-size-heading-1: var(--spacing-15);
}
/* Language Modes */
.language-jp {
--jp-abcvoice: 'ABCvoice JP';
--jp-abcvoice-display: 'ABCvoice Display';
}
All the stages that are mentioned above can be triggered via npm script one after another
npm run sync-figma-to-tokens (Stage1)
npm run build:modes (Stage 2)
npm run postprocess:modes (Stage 3)
Alternative Approach
In lieu of the approach that we discussed, we can also utilise Token Studio plugin (formerly Figma Tokens plugin) that an export design tokens to CSS with variable mappings. It supports:
JSON Token Export: Exports tokens in Style Dictionary-compatible JSON format with alias references
{ "color": { "global": { "white": { "primary": { "value": "#ffffff", "type": "color" } } }, "background": { "base": { "value": "{color.global.white.primary}", "type": "color" } } } }
Multi-Mode/Theme Support: Organizes tokens into theme sets (Light, Dark, etc.)
Style Dictionary Integration: Token Studio JSON can be processed by Style Dictionary to generate CSS with var() mappings
However we need to weigh in the pros and cons prior to deciding on which one to opt for
Advantages | Limitations |
Version Control: JSON tokens live in Git before Figma sync | Manual Sync: Requires plugin action to push/pull tokens |
Conclusion
As a recommendation, the pipeline (Figma Variables -> API -> JSON -> Style Dictionary -> CSS -> Postprocessor) is more robust and maintainable for teams that can manage the technical setup. Token Studio adds designer autonomy but introduces manual sync friction and adds costs
