DEV Community

Alexander
Alexander

Posted on

Auto-generating Tailwind config from Figma variables via GitHub Actions

The Slack message that started it all

"Hey Alex, the new brand colours are not working in Tailwind. I tried using bg-brand-primary-500 and it just silently fails."

I got this message from a junior developer last month. I checked the Figma file. The design team had absolutely updated the colours. I checked our main branch. The Tailwind config was completely out of date. We had a massive gap between our design variables and our codebase.

Somebody was supposed to manually copy the hex codes from Figma into our tailwind.config.ts file. That person forgot. Honestly I do not blame them. Manually copying hex codes is a terrible job. It is boring and prone to human error.

We needed a way to automate this entirely. When a designer updates a variable in Figma, it should automatically update our Tailwind configuration. No copy pasting allowed.

Let us look at how you can build a pipeline to do exactly this using TypeScript and GitHub Actions.

Parsing the raw Figma JSON

Figma variables usually export as a nested JSON structure. If you use the standard W3C format, it looks something like this.

{
  "brand": {
    "primary": {
      "500": {
        "value": "#3b82f6",
        "type": "color"
      },
      "600": {
        "value": "#2563eb",
        "type": "color"
      }
    }
  },
  "spacing": {
    "sm": {
      "value": "0.5rem",
      "type": "dimension"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Tailwind expects a flat or slightly nested object for its theme configuration. We cannot just pass this W3C JSON directly into Tailwind. We need to strip out the value and type keys. We also need to flatten the structure slightly.

Here is a simple Node script to transform this JSON into a Tailwind friendly format.

import fs from 'node:fs'
import path from 'node:path'

interface TokenNode {
  value?: string
  type?: string
  [key: string]: any
}

function processTokens(obj: Record<string, any>): Record<string, any> {
  const result: Record<string, any> = {}

  for (const key in obj) {
    const node = obj[key]

    if (node.value !== undefined) {
      result[key] = node.value
    } else if (typeof node === 'object') {
      result[key] = processTokens(node)
    }
  }

  return result
}

function generateTailwindTheme() {
  const rawData = fs.readFileSync(path.resolve('./tokens.json'), 'utf-8')
  const tokens = JSON.parse(rawData)

  const theme = {
    colors: processTokens(tokens.brand || {}),
    spacing: processTokens(tokens.spacing || {})
  }

  const fileContent = `// Auto-generated from Figma variables\nexport const theme = ${JSON.stringify(theme, null, 2)}`

  fs.writeFileSync(path.resolve('./src/theme.ts'), fileContent)
  console.log('Tailwind theme generated successfully.')
}

generateTailwindTheme()
Enter fullscreen mode Exit fullscreen mode

This script reads your raw design tokens. It recursively strips out the extra metadata. It writes a clean TypeScript file containing just the values you need.

Injecting it into Tailwind

Now we have a clean theme.ts file. We just need to tell Tailwind to use it.

Open your tailwind.config.ts file. You can import the generated theme directly into your configuration.

import type { Config } from 'tailwindcss'
import { theme } from './src/theme'

const config: Config = {
  content: [
    './src/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      colors: theme.colors,
      spacing: theme.spacing,
    },
  },
  plugins: [],
}

export default config
Enter fullscreen mode Exit fullscreen mode

This is so much better than hardcoding values. Your Tailwind config is now a direct reflection of your design tokens.

Wiring up the CI pipeline

The real magic happens when you automate this script. You do not want developers running this locally. You want it to happen automatically when new tokens arrive.

We can set up a GitHub Action to run our transformation script whenever the tokens.json file changes. This ensures our Tailwind config is always up to date before the code merges.

name: Generate Tailwind Config

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

jobs:
  build-theme:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Generate Theme
        run: npx tsx scripts/generate-theme.ts

      - name: Commit changes
        uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: "chore: update tailwind theme from tokens"
          file_pattern: "src/theme.ts"
Enter fullscreen mode Exit fullscreen mode

This pipeline watches for changes to your raw tokens. It runs the Node script we wrote earlier. It then automatically commits the updated theme.ts file back to the repository.

Your designers update Figma. The tokens get exported. The pipeline runs. Your Tailwind classes just work.

The easier way to export from Figma

The pipeline above is great. But it assumes you already have a magical way to get your Figma variables into that tokens.json file in your repository.

Getting tokens out of Figma is usually the hardest part. You often have to rely on complex custom scripts or ask designers to manually export files. Both options are terrible.

I actually built a plugin to solve this exact problem. It is called Design System Sync. It takes your Figma variables and automatically creates a Pull Request in GitHub or Bitbucket.

You can export directly to W3C format. You can also have the plugin generate CSS Variables or raw code for you. It handles everything right from the Figma canvas. You just click export and review the PR.

If you want to try it out, you can find the website at https://ds-sync.netlify.app?utm_source=devto&utm_medium=post&utm_campaign=bot.

You can also grab the plugin directly from the Figma Community here: https://www.figma.com/community/plugin/1561389071519901700?utm_source=devto&utm_medium=post&utm_campaign=bot.

Building a solid design system pipeline takes time. Automating the small things like Tailwind configurations makes a massive difference for your team. Give it a try and let me know how it goes.

Top comments (0)