Skip to content

thehashton/Tokensmith-cli

Repository files navigation

Tokensmith (Python + Figma)

Sync Figma design tokens into CSS, Tailwind, and TypeScript — without the copy-paste tax.

Tokensmith is a Python CLI for senior frontend teams who keep a Figma file as the source of truth for colors, spacing, radii, and typography. It pulls local variables (including light/dark modes) from the Figma Variables API, normalizes them into a typed token model, and exports idiomatic code artifacts you can commit and review.

Figma variables  ──pull──►  .tokensmith/snapshot.json  ──export──►  tokens.css / tokens.ts / tailwind.tokens.js
                                      │
                                      └──diff──►  Rich table of added / removed / modified tokens

The problem

Design systems drift. A designer updates --color-primary in Figma; engineering still ships last quarter’s hex in globals.css. Manual sync is slow, error-prone, and invisible in code review.

Tokensmith makes the pipeline explicit:

  1. Pull variables from a real Figma file (PAT + file key).
  2. Export to CSS custom properties, a Tailwind config partial, or a typed tokens.ts.
  3. Diff the latest pull against the last export so CI (or you) can fail when tokens drift.

Your Figma personal access token is never written to disk, committed, or logged. Pass --token or set FIGMA_TOKEN.

Install

Requires Python 3.11+.

With uv (recommended)

# from this repo
uv sync
uv run tokensmith --help

# or install as a tool
uv tool install .

With pipx

pipx install .
tokensmith --help

Editable install (pip)

pip install -e ".[dev]"

30-second usage

Create a tokensmith.toml in your project (see tokensmith.toml.example):

file_key = "YOUR_FIGMA_FILE_KEY"
default_format = "css"
output_path = "tokens"
default_mode = "Light"

[mode_selectors]
Dark = ".dark, [data-theme='dark']"

Export a Figma personal access token with the file_variables:read scope, then:

export FIGMA_TOKEN="figd_••••"   # never commit this

tokensmith pull --file-key YOUR_FIGMA_FILE_KEY
# Pulled 12 token(s) across 2 mode(s) → .tokensmith/snapshot.json
# Modes: Dark, Light

tokensmith export --format css
# Wrote tokens/tokens.css
# Exported 12 token(s) as css.

tokensmith export --format ts
# Wrote tokens/tokens.ts

tokensmith export --format tailwind
# Wrote tokens/tailwind.tokens.js

Example CSS output (modes become separate scopes):

/* Generated by tokensmith — do not edit by hand. */

:root {
  --color-accent: #2563eb;
  --color-primary: #2563eb;
  --color-surface: #ffffff;
  --radius-lg: 12px;
  --spacing-md: 16px;
  --typography-body: Inter;
}

[data-theme="dark"] {
  --color-accent: #60a5fa;
  --color-primary: #60a5fa;
  --color-surface: #12171c;
  --radius-lg: 12px;
  --spacing-md: 16px;
  --typography-body: Inter;
}

After a designer changes tokens in Figma:

tokensmith pull
tokensmith diff
# 1 change(s) (+0 / -0 / ~1)
#
# ┏━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
# ┃ Status     ┃ Token         ┃ Mode  ┃ Before        ┃ After         ┃
# ┡━━━━━━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
# │ modified   │ Color/Primary │ Light │ color:#2563eb │ color:#111111 │
# └────────────┴───────────────┴───────┴───────────────┴───────────────┘

tokensmith diff exits 1 when changes exist (CI-friendly) and 0 when the export is in sync.

Commands

Command Purpose
tokensmith pull Fetch local variables → .tokensmith/snapshot.json
tokensmith export Write css / tailwind / ts from the snapshot
tokensmith diff Compare latest pull vs last export (Rich table)
tokensmith version Print package version

Flags of note:

  • --file-key / file_key in config
  • --token or FIGMA_TOKEN (never stored)
  • --format css|tailwind|ts, --all for every format in config
  • --output to override output_path

Modes (light / dark)

Figma variable modes are first-class:

  • CSS — default mode → :root; other modes → [data-theme="…"] (override with [mode_selectors]).
  • TypeScript — one as const export per mode (tokensLight, tokensDark, …) plus tokensByMode.
  • Tailwind — default mode under theme.extend; other modes under a modes map for your own theme plugin.

Configuration

tokensmith.toml (repo root):

Key Description
file_key Default Figma file key
default_format css, tailwind, or ts
output_path Directory for generated files (default tokens)
formats Formats used by export --all
default_mode Mode mapped to :root / primary export
mode_selectors Custom CSS selectors per mode name
aliases Rename Figma variable names on export

Local state (gitignored by default):

  • .tokensmith/snapshot.json — last pull
  • .tokensmith/export_manifest.json — last export (diff baseline)

Wire into CI (token drift → PR)

Tokensmith is built so a scheduled job can fail (or open a PR) when Figma moves and the repo does not.

Example GitHub Actions sketch:

name: Sync design tokens
on:
  schedule:
    - cron: "0 8 * * 1-5"   # weekdays 08:00 UTC
  workflow_dispatch:

jobs:
  tokens:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v5
      - run: uv sync
      - name: Pull & export
        env:
          FIGMA_TOKEN: ${{ secrets.FIGMA_TOKEN }}
        run: |
          uv run tokensmith pull
          uv run tokensmith export --all
      - name: Detect drift
        id: drift
        run: |
          if uv run tokensmith diff; then
            echo "changed=false" >> "$GITHUB_OUTPUT"
          else
            echo "changed=true" >> "$GITHUB_OUTPUT"
          fi
      - name: Open PR when tokens change
        if: steps.drift.outputs.changed == 'true'
        uses: peter-evans/create-pull-request@v6
        with:
          commit-message: "chore(tokens): sync from Figma"
          title: "chore(tokens): sync from Figma"
          body: |
            Automated token sync via tokensmith.
            Review the CSS / Tailwind / TS diffs carefully.
          branch: chore/tokensmith-sync

Store the Figma PAT as FIGMA_TOKEN in repository secrets. Never echo it in logs.

This repo’s own CI (.github/workflows/ci.yml) runs ruff and pytest on push/PR — all Figma calls are mocked; no live API access is required.

Development

uv sync --extra dev
uv run ruff check src tests
uv run pytest

Tests use httpx.MockTransport and fixtures under tests/fixtures/ so CI never hits api.figma.com.

To exercise a live Figma file:

export FIGMA_TOKEN="figd_…"
uv run tokensmith pull --file-key YOUR_FILE_KEY

Roadmap

Not implemented yet — candidates for a follow-up:

  • tokensmith watch — poll Figma on an interval and auto re-export
  • First-class GitHub API integration to open a PR when tokens change (today: compose with create-pull-request as above)
  • Support for Figma styles (not just variables) for older files

License

MIT — see LICENSE.

About

Python CLI that syncs Figma design tokens into CSS, Tailwind, and TypeScript Resources

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages