<link rel="stylesheet" href="assets/fonts/inter.css"> Advanced Design Tokens in Angular: Figma Variables to… | Viral Patel Studio Skip to main content

Advanced Design Tokens in Angular: Figma Variables to Style Dictionary 2025

Dec 17, 2025By Viral Patel

Master the design token pipeline in 2025: export Figma Variables, transform them with Style Dictionary, and use tokens as CSS properties in Angular apps.

Design Tokens in Angular: Figma Variables to Style Dictionary 2025

Design Token Pipeline Figure 1: The complete design token pipeline from Figma Variables to Angular CSS, SCSS, and TypeScript

Why Design Tokens Transform Your Design System

Without design tokens, your codebase and Figma file drift apart within weeks of launch. A designer changes the primary color; a developer doesn't see the update; the product ships with inconsistent colors. Design tokens solve this by creating a single source of truth that flows automatically from Figma to code.

In 2025, the toolchain is mature: Figma Variables provides the authoring surface, W3C DTCG defines the transport format, Style Dictionary transforms tokens into CSS/SCSS/TypeScript, and Angular consumes the output through standard CSS custom properties. This guide walks you through every step.


Understanding Token Categories

Before building the pipeline, define your token architecture. Most design systems use a three-layer model:

Layer Example Purpose
Primitive (raw) color.blue.500 = #3b82f6 Raw values; never used directly in components
Semantic color.action.primary = {color.blue.500} Intent-based aliases to primitives
Component button.background.default = {color.action.primary} Component-specific tokens that alias semantic tokens

This layering enables dark mode (swap semantic tokens) and rebranding (swap primitive tokens) without touching component code.


Step 1: Define Tokens in W3C DTCG Format

The W3C Design Token Community Group (DTCG) format structures tokens as JSON with $value and $type fields:

{
  "color": {
    "primitive": {
      "blue": {
        "500": { "$value": "#3b82f6", "$type": "color" },
        "600": { "$value": "#2563eb", "$type": "color" },
        "700": { "$value": "#1d4ed8", "$type": "color" }
      },
      "neutral": {
        "0":   { "$value": "#ffffff", "$type": "color" },
        "900": { "$value": "#111827", "$type": "color" }
      }
    },
    "semantic": {
      "action": {
        "primary":  { "$value": "{color.primitive.blue.600}", "$type": "color" },
        "hover":    { "$value": "{color.primitive.blue.700}", "$type": "color" }
      },
      "background": {
        "page":   { "$value": "{color.primitive.neutral.0}", "$type": "color" },
        "subtle": { "$value": "#f9fafb", "$type": "color" }
      },
      "text": {
        "primary":   { "$value": "{color.primitive.neutral.900}", "$type": "color" },
        "secondary": { "$value": "#6b7280", "$type": "color" }
      }
    }
  },
  "spacing": {
    "1": { "$value": "4px",  "$type": "dimension" },
    "2": { "$value": "8px",  "$type": "dimension" },
    "4": { "$value": "16px", "$type": "dimension" },
    "6": { "$value": "24px", "$type": "dimension" },
    "8": { "$value": "32px", "$type": "dimension" }
  }
}

Step 2: Set Up Figma Variables

Organize your Figma Variables into collections that mirror your token architecture:

  1. Primitives — one mode, raw values only
  2. Semantic — two modes: Light and Dark
  3. Component — references to semantic tokens

Naming Convention

Use consistent naming that maps predictably to CSS custom properties:

Figma Variable Name CSS Custom Property SCSS Variable TypeScript
color/semantic/action/primary --color-semantic-action-primary $color-semantic-action-primary ColorSemanticActionPrimary
spacing/4 --spacing-4 $spacing-4 Spacing4
font-size/body-md --font-size-body-md $font-size-body-md FontSizeBodyMd

Export Path A: Tokens Studio Plugin

Install the Tokens Studio for Figma plugin, connect to a GitHub repository, and push token JSON automatically on save. The output is already in a Style Dictionary-compatible format.

Export Path B: Figma REST API

For automated CI pipelines, use the Figma API directly:

curl -H "X-Figma-Token: $FIGMA_ACCESS_TOKEN" \
  "https://api.figma.com/v1/files/$FIGMA_FILE_ID/variables/local" \
  -o figma-variables.json

Transform the raw API response to DTCG format with a Node.js script:

// scripts/export-figma-tokens.mjs
import { writeFileSync } from 'fs';

const FIGMA_TOKEN = process.env.FIGMA_ACCESS_TOKEN;
const FILE_ID = process.env.FIGMA_FILE_ID;

function rgbToHex({ r, g, b }) {
  const toHex = (v) => Math.round(v * 255).toString(16).padStart(2, '0');
  return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
}

async function exportTokens() {
  const res = await fetch(
    `https://api.figma.com/v1/files/${FILE_ID}/variables/local`,
    { headers: { 'X-Figma-Token': FIGMA_TOKEN } }
  );
  const { meta } = await res.json();
  const tokens = {};

  for (const variable of Object.values(meta.variables)) {
    const { name, resolvedType, valuesByMode } = variable;
    const path = name.split('/');
    let node = tokens;
    for (const segment of path.slice(0, -1)) {
      node[segment] ??= {};
      node = node[segment];
    }
    const key = path.at(-1);
    const rawValue = Object.values(valuesByMode)[0];
    node[key] = {
      $type: resolvedType === 'COLOR' ? 'color' : 'dimension',
      $value: resolvedType === 'COLOR' ? rgbToHex(rawValue) : `${rawValue}px`,
    };
  }

  writeFileSync('tokens/tokens.json', JSON.stringify(tokens, null, 2));
  console.log('Tokens exported successfully');
}

exportTokens();

Step 3: Configure Style Dictionary

Style Dictionary transforms your token JSON into platform-specific output. Create sd.config.mjs in your project root:

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

const sd = new StyleDictionary({
  source: ['tokens/**/*.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      prefix: '',
      buildPath: 'src/assets/tokens/',
      files: [{
        destination: 'tokens.css',
        format: 'css/variables',
        selector: ':root',
        filter: (token) => token.attributes.category !== 'dark',
      }],
    },
    css_dark: {
      transformGroup: 'css',
      buildPath: 'src/assets/tokens/',
      files: [{
        destination: 'tokens-dark.css',
        format: 'css/variables',
        selector: '[data-theme="dark"]',
        filter: (token) => token.path.includes('semantic') && token.filePath.includes('dark'),
      }],
    },
    scss: {
      transformGroup: 'scss',
      buildPath: 'src/styles/',
      files: [{
        destination: '_tokens.scss',
        format: 'scss/variables',
      }],
    },
    ts: {
      transformGroup: 'js',
      buildPath: 'src/app/tokens/',
      files: [{
        destination: 'tokens.ts',
        format: 'javascript/es6',
      }],
    },
  },
});

await sd.buildAllPlatforms();

Run the build:

node sd.config.mjs

Generated Output

src/assets/tokens/tokens.css:

:root {
  --color-primitive-blue-500: #3b82f6;
  --color-primitive-blue-600: #2563eb;
  --color-semantic-action-primary: #2563eb;
  --color-semantic-text-primary: #111827;
  --spacing-4: 16px;
}

src/assets/tokens/tokens-dark.css:

[data-theme="dark"] {
  --color-semantic-action-primary: #60a5fa;
  --color-semantic-background-page: #111827;
  --color-semantic-text-primary: #f9fafb;
}

src/styles/_tokens.scss:

$color-semantic-action-primary: #2563eb;
$color-semantic-text-primary: #111827;
$spacing-4: 16px;

src/app/tokens/tokens.ts:

export const ColorSemanticActionPrimary = '#2563eb';
export const ColorSemanticTextPrimary = '#111827';
export const Spacing4 = '16px';

Step 4: Integrate Tokens in Angular

Global Styles

Import both CSS token files in angular.json or styles.scss. Load the same stylesheet in your component workshop too, so the Angular Storybook setup renders components and token swatches with real values:

// src/styles.scss
@import 'assets/tokens/tokens.css';
@import 'assets/tokens/tokens-dark.css';
@import 'styles/tokens';  // SCSS variables

Component Styles

Use CSS custom properties in component stylesheets for automatic dark mode:

// button.component.scss
.ds-button {
  background-color: var(--color-semantic-action-primary);
  color: var(--color-semantic-text-inverse);
  padding: var(--spacing-2) var(--spacing-4);
  
  &:hover {
    background-color: var(--color-semantic-action-hover);
  }
}

Use SCSS variables when you need compile-time values (e.g., breakpoints):

// layout.component.scss
@use 'styles/tokens' as t;

.ds-layout {
  max-width: t.$breakpoint-lg;
  padding: t.$spacing-4 t.$spacing-6;
}

TypeScript Constants with Path Aliases

Configure a TypeScript path alias to import token constants cleanly:

// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@tokens": ["src/app/tokens/tokens.ts"]
    }
  }
}
// In any component
import { ColorSemanticActionPrimary } from '@tokens';

// Useful for programmatic theming, chart colors, canvas rendering
const chartColor = ColorSemanticActionPrimary;

Step 5: Implement ThemeService

A ThemeService manages dark/light mode switching by toggling the data-theme attribute:

// src/app/services/theme.service.ts
import { Injectable, signal, effect } from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { inject } from '@angular/core';

export type Theme = 'light' | 'dark';

@Injectable({ providedIn: 'root' })
export class ThemeService {
  private document = inject(DOCUMENT);
  
  readonly theme = signal<Theme>(this.getInitialTheme());

  constructor() {
    effect(() => {
      this.applyTheme(this.theme());
    });
  }

  setTheme(theme: Theme): void {
    this.theme.set(theme);
    localStorage.setItem('theme', theme);
  }

  toggleTheme(): void {
    this.setTheme(this.theme() === 'light' ? 'dark' : 'light');
  }

  private getInitialTheme(): Theme {
    const stored = localStorage.getItem('theme') as Theme | null;
    if (stored) return stored;
    return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
  }

  private applyTheme(theme: Theme): void {
    this.document.documentElement.setAttribute('data-theme', theme);
  }
}

Step 6: Automate Token Sync with GitHub Actions

Run token export and Style Dictionary build on a schedule to keep tokens in sync with Figma:

# .github/workflows/sync-tokens.yml
name: Sync Design Tokens from Figma

on:
  workflow_dispatch:
  repository_dispatch:
    types: [figma-tokens-updated]
  schedule:
    - cron: '0 9 * * 1'  # Every Monday at 9:00 AM UTC

jobs:
  sync-tokens:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Export tokens from Figma
        run: node scripts/export-figma-tokens.mjs
        env:
          FIGMA_ACCESS_TOKEN: ${{ secrets.FIGMA_ACCESS_TOKEN }}
          FIGMA_FILE_ID: ${{ secrets.FIGMA_FILE_ID }}
      
      - name: Build Style Dictionary
        run: |
          npm ci
          node sd.config.mjs
      
      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v6
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          commit-message: 'chore(tokens): sync design tokens from Figma'
          branch: 'chore/sync-design-tokens'
          title: 'chore(tokens): design token sync from Figma'
          body: |
            Automated design token sync from Figma Variables.
            Review the token changes before merging.

Figma can trigger this via webhook → GitHub repository_dispatch when variables change.


Common Token Pipeline Mistakes

Mistake Impact Fix
Using primitive tokens directly in components Breaks dark mode; no semantic meaning Always use semantic token aliases
Hardcoding hex values alongside tokens Creates drift when tokens update Enforce via ESLint no-hardcoded-color rule
Single-mode tokens for dark/dark support Dark mode requires full rebuild instead of CSS swap Author light+dark modes in Figma Variables
Missing $type in DTCG format Style Dictionary can't apply correct transforms Always declare $type on every token
Syncing tokens manually Tokens drift from Figma within days Automate with the GitHub Actions workflow
No path alias for TypeScript tokens Brittle relative imports break on refactor Configure @tokens alias in tsconfig.json
Exposing primitive tokens in public API Consumers bypass semantic layer Export only semantic and component tokens

The Result: Zero-Drift Design

With this pipeline in place:

  • A designer changes the brand primary color in Figma → Monday morning, a PR appears in GitHub → engineer reviews and merges → components automatically update across the entire Angular app with no manual code changes.

Design tokens aren't just a developer convenience — they're the contract that keeps your design system and your codebase speaking the same language.