design-token-extract
Paste into Claude Code, Claude.ai, the desktop app, or the browser extension — ⌘/Ctrl+V. ✓ Copied. Switch to Claude and paste — ⌘/Ctrl+V.
Design Token Extraction
Export the complete token layer from Figma Variables to code, validate contrast at extraction time, and generate every format your project consumes. Follows the W3C DTCG 2025.10 stable specification throughout.
Usage
/design-token-extract [figma-url] [options]
If no URL is provided, the skill uses the current Figma selection (Figma MCP active session).
Options:
| Flag | Values | Default | Description |
|---|---|---|---|
--out |
directory | ./tokens/ |
Where to write output files |
--formats |
comma-separated | css,style-dictionary |
Output formats to generate |
--modes |
comma-separated | light |
Variable modes (collections) to export |
--contrast-gate |
aa, aaa, off |
aa |
WCAG contrast gate for semantic color pairs |
--prefix |
string | `` | Optional prefix for CSS custom property names |
Available formats: css, tailwind, scss, dotnet, style-dictionary, dtcg-json
Examples:
/design-token-extract https://figma.com/file/DS123/RBA-Design-System
/design-token-extract --modes light,dark --formats css,tailwind,scss --out src/tokens/
/design-token-extract --formats dotnet --contrast-gate aaa
Phase 1 — Read Figma Variables via MCP
Call get_variable_defs to retrieve the full variable set from the file:
get_variable_defs(node_url: "<figma-url>")
Extract:
- Variable collections — each collection is a token group (e.g.
Color,Spacing,Typography) - Modes — each collection can have multiple modes (e.g.
Light,Dark,Brand A,Brand B) - Variable name and path — Figma uses
/as separator (e.g.color/interactive/primary) - Resolved values — per mode: hex color, number (for spacing/size), string (for font family)
- Variable type —
COLOR,FLOAT,STRING,BOOLEAN - Scopes — Figma variable scope hints (e.g.
FILL,STROKE,TEXT_CONTENT) - Code syntax — if the design team has set CSS variable names, prefer those
Phase 2 — Classify Token Tier
For each variable, determine its tier based on the name path and Figma group conventions:
| Tier | Name pattern examples | DTCG treatment |
|---|---|---|
| Primitive | color/blue/500, spacing/4, font/family/base |
Store as $value literals; NOT exposed as CSS custom properties by default |
| Semantic | color/interactive/primary, color/feedback/error, spacing/component/gap |
Expose as CSS custom properties; reference primitives via DTCG {primitive.path} syntax |
| Component | button/bg-default, input/border-error |
Expose as CSS custom properties; reference semantic tokens |
If the Figma Variable names do not clearly distinguish tiers, infer:
- Variables whose value is a direct raw color/number = primitive
- Variables whose value is another variable (alias) = semantic or component
Phase 3 — Build DTCG Token Structure
Output tokens.json (or tokens/<collection>.tokens.json if multi-file output is preferred).
DTCG 2025.10 format:
{
"color": {
"blue": {
"500": {
"$type": "color",
"$value": "#0057B8",
"$description": "Primary brand blue — primitive, do not use directly in components"
}
},
"interactive": {
"primary": {
"$type": "color",
"$value": "{color.blue.500}",
"$description": "Default background for primary interactive elements. Contrast pair: on-interactive-primary (#fff) = 6.1:1 AA PASS"
},
"primary-dark": {
"$type": "color",
"$value": "#003D80",
"$description": "Dark mode override for primary interactive background. Contrast pair: on-interactive-primary-dark (#fff) = 8.4:1 AA PASS"
}
}
},
"spacing": {
"4": {
"$type": "dimension",
"$value": "1rem",
"$description": "Base spacing unit (16px). Maps to Figma spacing/4 variable."
}
},
"typography": {
"body-md": {
"$type": "typography",
"$value": {
"fontFamily": "{typography.font-family.base}",
"fontSize": "1rem",
"fontWeight": "400",
"lineHeight": "1.5",
"letterSpacing": "0"
}
}
}
}
Phase 4 — Contrast Validation (semantic color pairs)
For every semantic color token that will be used as a text/background pair, compute the WCAG
contrast ratio and embed it in the $description. Flag any failing pairs before writing files.
Finding contrast pairs
Identify pairs by convention:
color/on-<X>is the foreground forcolor/<X>backgroundcolor/text/<X>oncolor/surface/<X>color/icon/<X>oncolor/surface/<X>
Contrast formula
relativeLuminance(hex):
rgb = [R,G,B] / 255
linear = x <= 0.04045 ? x/12.92 : ((x+0.055)/1.055)^2.4
L = 0.2126*R_linear + 0.7152*G_linear + 0.0722*B_linear
contrast(L1, L2) = (lighter + 0.05) / (darker + 0.05)
Gates
| Text type | Minimum | SC |
|---|---|---|
| Body text (< 18pt / < 14pt bold) | 4.5:1 | 1.4.3 AA |
| Large text (≥ 18pt / ≥ 14pt bold) | 3:1 | 1.4.3 AA |
| UI components (border, icon, focus ring) | 3:1 | 1.4.11 |
| Enhanced body text | 7:1 | 1.4.6 AAA (if --contrast-gate aaa) |
Output a contrast report table:
## Contrast Report
| Pair | Foreground | Background | Ratio | Gate | Result |
|------|-----------|------------|-------|------|--------|
| on-interactive-primary / interactive-primary | #ffffff | #0057B8 | 6.1:1 | 4.5:1 | PASS |
| on-interactive-disabled / interactive-disabled | #6B6B6B | #C4C4C4 | 3.2:1 | 4.5:1 | FAIL* |
* FAIL entries block generation by default. Pass --contrast-gate off to write anyway with
warnings embedded in the token file $description.
Phase 5 — Generate Output Formats
5.1 CSS custom properties (css)
Write tokens/tokens.css (or tokens.css in --out directory):
:root {
/* === Semantic: Color === */
--color-interactive-primary: #0057B8;
--color-on-interactive-primary: #ffffff;
--color-focus-ring: #004A9F;
/* === Semantic: Spacing === */
--spacing-1: 0.25rem;
--spacing-2: 0.5rem;
--spacing-4: 1rem;
/* === Typography === */
--typography-body-font-family: 'Inter', sans-serif;
--typography-body-md-font-size: 1rem;
--typography-body-md-line-height: 1.5;
}
For dark mode, generate [data-theme='dark'] or @media (prefers-color-scheme: dark) blocks
depending on the project's theming strategy (detect from existing CSS if possible).
5.2 Tailwind theme extension (tailwind)
Write tokens/tailwind-tokens.ts:
// tailwind.config.ts — extend this into your config
export const rbaTokens = {
colors: {
'interactive-primary': 'var(--color-interactive-primary)',
'on-interactive-primary': 'var(--color-on-interactive-primary)',
'focus-ring': 'var(--color-focus-ring)',
},
spacing: {
'1': 'var(--spacing-1)',
'2': 'var(--spacing-2)',
'4': 'var(--spacing-4)',
},
fontFamily: {
body: ['var(--typography-body-font-family)', 'sans-serif'],
},
fontSize: {
'body-md': ['var(--typography-body-md-font-size)', { lineHeight: 'var(--typography-body-md-line-height)' }],
},
};
5.3 SCSS variable map (scss)
Write tokens/_tokens.scss:
// Auto-generated from Figma Variables — do not edit manually
// Run /design-token-extract to regenerate
// Color
$color-interactive-primary: var(--color-interactive-primary) !default;
$color-on-interactive-primary: var(--color-on-interactive-primary) !default;
$color-focus-ring: var(--color-focus-ring) !default;
// Spacing
$spacing-1: var(--spacing-1) !default;
$spacing-4: var(--spacing-4) !default;
// Typography
$typography-body-font-family: var(--typography-body-font-family) !default;
5.4 .NET token class (dotnet)
Write Tokens/DesignTokens.cs:
/// <summary>
/// Design tokens generated from Figma Variables.
/// Auto-generated — do not edit manually. Run /design-token-extract to regenerate.
/// </summary>
public static class DesignTokens
{
public static class Color
{
public const string InteractivePrimary = "var(--color-interactive-primary)";
public const string OnInteractivePrimary = "var(--color-on-interactive-primary)";
public const string FocusRing = "var(--color-focus-ring)";
}
public static class Spacing
{
public const string S1 = "var(--spacing-1)";
public const string S4 = "var(--spacing-4)";
}
}
5.5 Style Dictionary config (style-dictionary)
Write tokens/sd.config.json and tokens/tokens.json ready for npx style-dictionary build:
{
"source": ["tokens/tokens.json"],
"platforms": {
"css": {
"transformGroup": "css",
"prefix": "rba",
"buildPath": "src/styles/",
"files": [{ "destination": "tokens.css", "format": "css/variables" }]
},
"js": {
"transformGroup": "js",
"buildPath": "src/tokens/",
"files": [{ "destination": "tokens.js", "format": "javascript/es6" }]
}
}
}
Phase 6 — Output Summary
## Token Extraction Summary
**Figma file:** <url>
**Collections exported:** Color, Spacing, Typography, Shadow
**Modes exported:** Light, Dark
**Token counts:**
- Primitive tokens: N
- Semantic tokens: N
- Component tokens: N
**Contrast validation:** N pairs checked
- PASS: N
- FAIL: N (listed above — resolve before using in components)
**Files generated:**
- tokens/tokens.json (DTCG)
- tokens/tokens.css (CSS custom properties)
- tokens/tailwind-tokens.ts (Tailwind extension) [if requested]
- tokens/_tokens.scss (SCSS map) [if requested]
- Tokens/DesignTokens.cs (.NET class) [if requested]
- tokens/sd.config.json (Style Dictionary) [if requested]
**Next steps:**
- Fix any FAIL contrast pairs in Figma before the next extraction
- Run /design-system-sync periodically to catch drift
- Import tokens/tokens.css in your global stylesheet before component styles
Technical details
- Plugin
- design-to-code-builder
- Author
- Dakota Smith
- Skill name
design-token-extract- Maturity
- experimental
- Security review
- No security review
- Owners
-
- engineering
- design
- Argument hint
<figma-url-or-current-selection> [--out <dir>] [--formats css,tailwind,scss,dotnet,style-dictionary] [--modes light,dark,brand]- Allowed tools
-
ReadWriteEditGlobGrepmcp__figma-dev-mode-mcp-server__get_variable_defsmcp__figma-dev-mode-mcp-server__get_metadataBash(node *)Bash(npx style-dictionary *)
- Source
plugins/design-to-code-builder/skills/design-token-extract/SKILL.md
Edit this skill on GitHub — opens GitHub's web editor. No git knowledge needed.