This guide walks you through building a fully automated design tokens pipeline — from Figma variables to production-ready CSS, JavaScript, and Swift/Kotlin tokens — using Tokens Studio, Style Dictionary 4, and GitHub Actions. You can complete the foundational setup in three to five hours, and the pipeline will sync automatically every time your design team publishes a change.

What You'll Build

  • A Figma-connected token source using Tokens Studio (formerly Figma Tokens) that exports structured JSON
  • A Style Dictionary 4 configuration that transforms tokens into CSS custom properties, JS/TS constants, and platform-specific outputs
  • A GitHub Actions workflow that auto-generates and commits token files on every Figma publish
  • A versioned tokens package your frontend, iOS, and Android teams consume from a shared monorepo or npm registry

Prerequisites

  • Figma Professional or Organisation plan (Tokens Studio requires plugin access)
  • Node.js 20+ and pnpm 9+ installed locally
  • A GitHub repository — public or private works fine
  • Basic familiarity with JSON and the command line
  • Tokens Studio Pro licence ($16/month as of May 2026) for the GitHub sync feature

Step 1: Set Up Tokens Studio in Figma

Install the Tokens Studio plugin from the Figma Community. Open your design file, launch the plugin, and choose New Empty File to start fresh.

Why organise tokens into sets?

Sets let you layer tokens — primitives first, then semantic tokens that reference them. This mirrors how Style Dictionary resolves aliases and prevents circular dependencies later.

Create three sets inside the plugin:

  • primitive — raw values: colour hex codes, raw spacing numbers, font sizes
  • semantic — intent-based aliases: color.background.primary references primitive.blue.500
  • component — component-scoped overrides: button.padding.horizontal

Add a primitive colour token manually to verify the structure:

{
  "primitive": {
    "blue": {
      "500": { "value": "#3B5BDB", "type": "color" }
    }
  }
}

Expected result: The token appears in the canvas inspector when you apply it to a shape.

Common pitfall: Do not mix Figma native Variables and Tokens Studio tokens in the same file without a sync strategy. As of 2026, Tokens Studio supports two-way sync with Figma Variables, but enabling it mid-project can overwrite existing variable values.

Step 2: Connect Tokens Studio to GitHub

Go to the Tokens Studio settings panel and select Sync → GitHub. You will need a GitHub personal access token with repo scope.

Configure the sync settings:

  • Repository: your-org/design-tokens
  • Branch: main
  • File path: tokens/tokens.json

Click Save and Push. Tokens Studio will commit your current token JSON to the repository at that path.

What if the push fails with a 401 error?

Your personal access token either lacks repo scope or has expired. GitHub now defaults to fine-grained tokens — make sure you grant Contents: Read and Write for the specific repository under the fine-grained token settings.

Step 3: Install and Configure Style Dictionary 4

Style Dictionary 4 (released Q1 2025, stable as of 2026) introduced a fully async Node ESM API, replacing the legacy StyleDictionary.buildAllPlatforms() pattern. Use pnpm to install it:

pnpm add -D style-dictionary@^4

Create a sd.config.mjs file at the root of your repository:

import StyleDictionary from 'style-dictionary';

const sd = new StyleDictionary({
  source: ['tokens/tokens.json'],
  platforms: {
    css: {
      transformGroup: 'css',
      prefix: 'ds',
      buildPath: 'dist/css/',
      files: [
        {
          destination: 'variables.css',
          format: 'css/variables',
        },
      ],
    },
    js: {
      transformGroup: 'js',
      buildPath: 'dist/js/',
      files: [
        {
          destination: 'tokens.mjs',
          format: 'javascript/es6',
        },
      ],
    },
  },
});

await sd.buildAllPlatforms();
console.log('Tokens built successfully.');

Run the build locally to verify output:

node sd.config.mjs

Expected result: Two files appear — dist/css/variables.css and dist/js/tokens.mjs. The CSS file will contain lines like --ds-primitive-blue-500: #3B5BDB;.

When should you add custom transforms?

Add a custom transform when the built-in transformGroup does not match your naming convention. For example, Australian and Singaporean teams using a BEM-like token taxonomy often need a custom name transform. Register it before calling buildAllPlatforms:

sd.registerTransform({
  name: 'name/kebab-semantic',
  type: 'name',
  transform: (token) =>
    token.path.join('-').toLowerCase(),
});

Step 4: Add a Semantic Token Resolver

Style Dictionary resolves aliases automatically when you use the {} reference syntax in your token JSON. Update your tokens.json so semantic tokens reference primitives:

{
  "semantic": {
    "color": {
      "background": {
        "primary": {
          "value": "{primitive.blue.500}",
          "type": "color"
        }
      }
    }
  }
}

Re-run the build. Style Dictionary will output --ds-semantic-color-background-primary: #3B5BDB; — a resolved value, not a reference.

Pro tip: Keep primitive tokens out of your CSS output entirely. Add a filter to the CSS platform config so only semantic and component tokens are exported to variables.css. This reduces your CSS file size by roughly 40–60% on large design systems and prevents engineers from accidentally using raw primitives directly.

files: [
  {
    destination: 'variables.css',
    format: 'css/variables',
    filter: (token) => token.path[0] !== 'primitive',
  },
],

Step 5: Automate the Pipeline with GitHub Actions

Create .github/workflows/tokens.yml in your repository:

name: Build Design Tokens

on:
  push:
    paths:
      - 'tokens/tokens.json'

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v3
        with:
          version: 9

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'pnpm'

      - run: pnpm install --frozen-lockfile

      - run: node sd.config.mjs

      - name: Commit built tokens
        uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: 'chore: rebuild design tokens [skip ci]'
          file_pattern: 'dist/**'

Expected result: Every time Tokens Studio pushes a new tokens.json to main, GitHub Actions rebuilds the dist/ folder and commits the output automatically — no manual steps required.

Why include [skip ci] in the commit message?

Without it, the auto-commit triggers the workflow again, creating an infinite loop. The [skip ci] flag is recognised by GitHub Actions natively as of 2024 and prevents the loop at no extra cost.

Step 6: Consume Tokens in Your Frontend Project

The simplest consumption pattern is a direct import from the dist/css path if both repos share a monorepo. Otherwise, publish the tokens as a private npm package and install it in your apps.

In a Next.js 15 project, import global tokens in app/globals.css:

@import '@your-org/design-tokens/dist/css/variables.css';

Then reference tokens in any component:

.button-primary {
  background-color: var(--ds-semantic-color-background-primary);
  padding: var(--ds-component-button-padding-horizontal);
}

For React Native or mobile teams, output a tokens.json in Style Dictionary's json/nested format and let the iOS/Android engineers consume it through their own platform transform scripts.

At Lenka Studio, we implement this exact pipeline for clients building multi-platform products — the token source of truth in Figma eliminates the most common design–developer handoff breakdowns we see in early-stage SaaS products across Australia and Singapore.

Step 7: Version and Changelog Your Token Releases

Use Changesets with your monorepo to version the tokens package independently. When a semantic token value changes, bump a minor version. When a token is removed, bump a major version to signal a breaking change to consuming teams.

Add this script to package.json:

{
  "scripts": {
    "build:tokens": "node sd.config.mjs",
    "release": "changeset publish\