design-token-sync · diff
git:20260922.ece18df to git:20260922.f04381f
49 added, 17 removed. Audit A to A.
---
name: design-token-sync
- description: One-way push of brand-memory palette and typography to CSS custom properties, Tailwind config, TypeScript declarations, and a Figma variables JSON. Use when generating design tokens, syncing to Figma, or regenerating a Tailwind config.
+ 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.
---
- Reads `brand.json` (from brand-memory) and writes to `dist/tokens/` (default `outDir`):
-
- - `tokens.css`: `:root { --color-bg: … }` custom properties
- - `tailwind.config.js`: `theme.extend` colors + fontFamily
- - `tokens.d.ts`: TS union types + `export const` per color
- - `figma-variables.json`: Figma Variables REST API payload
-
- Each starts with `/* Generated by atelier design-token-sync. Do not edit by hand. */`. Never hand-edit; re-run the sync.
+ 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 plugins/atelier/skills/design-token-sync/index.mjs [projectRoot] # default cwd
+ 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
- import { syncTokens } from './plugins/atelier/skills/design-token-sync/index.mjs';
- const written = await syncTokens({ projectRoot: '/path/to/project', outDir: 'dist/tokens' }); // absolute paths
+ 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}"
```
- Pure emitters, `(cfg) → string`: `emitCss` (`./emitters/css.mjs`), `emitTailwind` (`./emitters/tailwind.mjs`), `emitDts` (`./emitters/dts.mjs`), `emitFigmaVariables` (`./emitters/figma.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.
- ## Figma
+ ## Options
- `palette` collection: each color `type: "COLOR"`. `typography` collection: each font `type: "STRING"`. Import via `POST /v1/files/:key/variables` with the payload, or a Figma plugin accepting the variables bulk-import format.
+ | 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` |
- Typography entries with an empty/falsy value are omitted from all outputs.
+ 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.