design-token-sync · git:20260922.f04381f · 2026-09-22 · sha256 14509dd9ce9d0197

design-token-sync git:20260922.f04381fA

Immutable. This exact content never changes and is served at /api/v1/blob/14509dd9ce9d0197.

---
name: design-token-sync
description: Generate design tokens from the atelier brand file (.atelier/brand.json): CSS custom properties, a Tailwind theme config, typed JS constants (tokens.js + tokens.d.ts) and a Figma Variables REST API body. Use when creating or regenerating design tokens, CSS variables, a Tailwind config, TypeScript color constants or Figma variables after brand colors or fonts change.
---

One-way: reads `.atelier/brand.json` (see brand-memory), writes token files. It never reads its outputs back and never calls Figma.

## Run

```bash
node "${CLAUDE_PLUGIN_ROOT}/bin/atelier" tokens              # project root = cwd, writes dist/tokens/
node "${CLAUDE_PLUGIN_ROOT}/bin/atelier" tokens --root ./site --out src/styles/tokens --targets css,dts
```

JS API. The skill directory is passed as an argument so the import also works with Windows paths:

```bash
node --input-type=module -e '
import { pathToFileURL } from "node:url";
const { syncTokens } = await import(pathToFileURL(process.argv[1] + "/index.mjs").href);
const files = await syncTokens({ projectRoot: ".", outDir: "dist/tokens", targets: ["css", "tailwind"] });
console.log(files.join("\n"));
' "${CLAUDE_SKILL_DIR}"
```

`syncTokens({ projectRoot, outDir, targets })` resolves to the absolute paths it wrote. The same module exports the pure emitters `emitCss`, `emitTailwind`, `emitDts`, `emitJs` and `emitFigmaVariables`: each takes a brand config object and returns the file content as a string.

## Options

| CLI | API | Default | Meaning |
|---|---|---|---|
| `[projectRoot]` or `--root <dir>` | `projectRoot` | cwd | Directory holding `.atelier/brand.json` |
| `--out <dir>` | `outDir` | `dist/tokens` | Output directory: absolute, or relative to the project root |
| `--targets <list>` | `targets` (array or comma string) | all | Any of `css`, `tailwind`, `dts`, `figma` |

An unknown or empty target list is a usage error. All files are rendered before any is written.

## Output

| Target | File | Contents |
|---|---|---|
| css | `tokens.css` | `:root` with `--color-<key>` per palette entry and `--font-<slot>` per font |
| tailwind | `tailwind.config.js` | ESM `export default { theme: { extend: { colors, fontFamily } } }` |
| dts | `tokens.js` | ESM: frozen `colors` and `fonts` objects, plus one `export const` per color |
| dts | `tokens.d.ts` | Types for `tokens.js`: `ColorName`, `FontName`, literal-typed `colors`, `fonts` and consts |
| figma | `figma-variables.json` | Request body for Figma `POST /v1/files/:file_key/variables` |

The CLI prints one absolute path per line; the API returns them in the order tokens.css, tailwind.config.js, tokens.d.ts, figma-variables.json, tokens.js (only the requested targets).

`tokens.css` starts with `/* Generated by atelier design-token-sync. Do not edit by hand. */`. `tailwind.config.js`, `tokens.js` and `tokens.d.ts` start with the same text as a `//` comment. `figma-variables.json` has no header (JSON has no comments). Do not hand-edit: change brand.json and re-run.

Use the Tailwind file as a preset (`import tokens from './dist/tokens/tailwind.config.js'`, then `presets: [tokens]`), or with Tailwind v4 via `@config`.

## Exit codes

- `0`: files written, or `--help`.
- `2`: usage error (usage printed to stderr), or a failure such as a missing or invalid brand.json (`atelier: <message>` on stderr).

## Notes

- Fonts are emitted as CSS font-family lists. `Silkscreen, 'Courier New', monospace` becomes `"Silkscreen", "Courier New", monospace`: family names are quoted and escaped, generic families (serif, sans-serif, monospace, system-ui, ui-monospace and the like) stay bare. Tailwind gets one array entry per family, with `sans-serif` (`monospace` for the mono slot) appended when the stack has no generic.
- Any palette key brand.json allows works (`brand-500`, `default`); object keys are quoted where needed. Named exports in tokens.js use safe identifiers: other keys are camelCased (`brand-500` -> `brand500`, `brand-primary` -> `brandPrimary`), reserved words and the names `colors` and `fonts` get a trailing `_` (`default` -> `default_`), and a name that is already taken gets another `_`. `colors['brand-500']` always works with the original key, and tokens.d.ts marks each renamed export with its key.
- Figma body: a `palette` collection of COLOR variables (`{ r, g, b, a }` from 0 to 1) and a `typography` collection of STRING variables holding the first family of each stack, each collection with one mode named `default`. To apply it: `curl -X POST -H "X-Figma-Token: $FIGMA_TOKEN" -H "Content-Type: application/json" --data @dist/tokens/figma-variables.json https://api.figma.com/v1/files/<file_key>/variables`. Figma allows this endpoint only for full members of Enterprise orgs, with a token that has the `file_variables:write` scope. Every change is a CREATE, so posting twice duplicates the collections.
- Empty typography values are skipped in every output. Every value is escaped for its format, so a brand.json value cannot break out of a string or a declaration.