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.
Installs into .claude/skills of the current project.
Are you the author of Design Token Sync?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/ethany33-design-token-sync)
---
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 module path is passed as an argument and turned into a file URL, so the import works with Windows, macOS and Linux paths:
```bash
node --input-type=module -e "const { pathToFileURL } = await import('node:url'); const m = await import(pathToFileURL(process.argv[1]).href); const files = await m.syncTokens({ projectRoot: '.', outDir: 'dist/tokens', targets: ['css', 'tailwind'] }); for (const f of files) console.log(f);" "${CLAUDE_SKILL_DIR}/index.mjs"
```
`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` |
| `-h`, `--help` | | | Print usage to stdout and exit 0 |
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) and the vendor keywords `-apple-system` and `BlinkMacSystemFont` stay bare. Tailwind gets one array entry per family, with `sans-serif` (`monospace` for the mono slot) appended when the stack has no generic family. The vendor keywords do not count as one, because a browser without them skips them.
- 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`, `fonts` and `Object` get a trailing `_` (`default` -> `default_`, `Object` -> `Object_`), 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.