snps-cli
v0.1.8
Published
CLI for Synapse
Readme
snps-cli
Command-line tool for syncing design-system entities — components, tokens, typography and icons — straight from Figma into your codebase, and for pulling ready-made UI components from a shadcn-style registry.
It reads a single synapse.config.json, fetches the source of truth from the Figma REST API, and writes generated SCSS/CSS mixins, TypeScript variant types, Markdown structure docs, SVG icons and token files to the paths you configure.
Install
npm i -D snps-cliThe binary is snps:
npx snps --help
# or, if installed globally
snps --helpQuick start
# 1. Create synapse.config.json in the current directory
npx snps init
# 2. Authenticate with Figma (saves a token into synapse.config.json)
npx snps login
# 3. Describe what to sync
npx snps add tokens --file <figmaFileKey>
npx snps add component button
# 4. Pull it from Figma
npx snps syncCommands
Run snps <command> --help for command-specific options.
General
| Command | Description |
| --- | --- |
| snps login | Authenticate with Figma via OAuth; the token is saved to synapse.config.json. |
| snps init | Scaffold a new synapse.config.json in the current directory. |
| snps push-errors [--no-clear] | Post accumulated sync errors back to the Figma file as comments. --no-clear keeps the local error log. |
add — write entity config to synapse.config.json
| Command | Description |
| --- | --- |
| snps add icons [--path] [--file] [-f] | Configure icons (paths.icons, config.icons.figmaFile). |
| snps add tokens [--path] [--file] [--component] [-f] | Configure tokens. Default frame name: Colors. |
| snps add tokens --group <name> [--prefix [value]] [--separator] [--output] [-f] | Add a token group (spacing, radius, …) with its own prefix — see Token groups. |
| snps add typography [--path] [--file] [--component] [--format] [-f] | Configure typography. --format accepts a CSV such as css,scss. |
| snps add component [name] [--all] [-f] | Download component source files from the components registry (see below). |
add componentis different from the others: instead of only editing config, it fetches real component files from a registry repository and writes them to your project, resolving each component's dependencies transitively.
sync — fetch from Figma and write to disk
| Command | Description |
| --- | --- |
| snps sync [-f] | Sync every configured entity: tokens, typography, icons and components. -f forces every one of them. |
| snps sync icons [--clear] [-f] | Fetch icons as SVGs. --clear empties the output dir first. |
| snps sync tokens [-f] | Fetch tokens and emit CSS/SCSS variables. |
| snps sync typography [-f] | Fetch typography and emit typography.css and/or typography.scss. |
| snps sync component [name] [--all] [-f] | Sync one component, or all configured components. |
Common options
| Option | Description |
| --- | --- |
| -f, --force | Bypass the cache (on sync) or overwrite existing config/files (on add). |
| -h, --help | Show help for a command. |
| --version | Print the CLI version. |
Sync results are cached by the Figma file's last-modified time, so re-running sync only re-downloads what actually changed. Use --force to ignore the cache.
Configuration
Everything lives in synapse.config.json at the root of the project you run snps from.
{
// Base output directories per entity type.
"paths": {
"components": "./",
"tokens": "./tokens/",
"icons": "./icons/",
"typography": "./typography/"
},
"config": {
// ── Components ──────────────────────────────────────────────
"components": {
// Global defaults, inherited by every component entry below.
"figmaFile": "<figmaFileKey>", // default file to read components from
"types": true, // emit <name>.types.ts
"structure": true, // emit <name>.md
"format": "scss", // "scss" | "css" | ["scss", "css"]
// One entry per component. The key is the Figma component name.
"button": {
"path": "./components/button", // where generated files go
// Optional per-component overrides:
"figmaFile": "<otherFileKey>", // read this component from another file
"typesPath": "./types", // override where <name>.types.ts goes
"structurePath": "./docs", // override where <name>.md goes
"types": true, // override the global toggle
"structure": true
},
"slider": { "path": "./components/slider" }
},
// ── Icons ───────────────────────────────────────────────────
"icons": { "figmaFile": "<figmaFileKey>" },
// ── Tokens ──────────────────────────────────────────────────
"tokens": {
"figmaFile": "<figmaFileKey>", // default file for every group
"componentName": "Colors", // single-source setup; ignored when "groups" is set
"separator": "-", // default glue between prefix and name
// Optional: several token sources, each its own Figma COMPONENT_SET.
// The key names the group and doubles as the default component name
// and the default prefix.
"groups": {
"variables": { "component": "Colors" }, // --bg-primary
"spacing": { "prefix": true, "separator": "_" }, // --spacing_xs
"radius": { "component": "Rounding", "prefix": "r" } // --r-xs
}
},
// ── Typography ──────────────────────────────────────────────
"typography": {
"figmaFile": "<figmaFileKey>",
"format": ["css", "scss"],
"componentName": "Typography",
"variables": true // emit CSS variables per property
}
},
// Populated by `snps login`.
"figma": { "token": "<figma-token>" }
}Token groups
Without groups, sync tokens reads a single Figma COMPONENT_SET (config.tokens.componentName, default Colors) and writes each variable exactly as it is named in Figma.
groups lets you read several sets, each with its own naming policy. This matters when a set uses short, self-contained names: a Spacing set with variants xs, s, m needs a prefix, while a color set already carries one in its names (bg-primary).
| Key | Default | Meaning |
| --- | --- | --- |
| component | the group key | COMPONENT_SET name in Figma (matched case-insensitively) |
| figmaFile | config.tokens.figmaFile | read this group from another file |
| prefix | false | false — no prefix; true — the group key; a string — a custom prefix |
| separator | - | glue between the prefix and the token name |
| output | tokens | base file name; the theme is appended as <output>.<theme>.css |
prefix and separator can also be set once on config.tokens as defaults for every group.
# spacing tokens from the "Spacings" set, emitted as --spacing_xs, --spacing_m, …
npx snps add tokens --group spacing --component Spacings --prefix --separator _Each variant in the set must have the property carrying its value bound to a Figma variable, on the variant itself rather than a nested layer — a fill for colors, the width for spacings, the corner radius for radii. The bound property decides which extractor runs. Shadow tokens are the exception: effects cannot be bound to a variable, so a variant carrying a visible shadow and no binding is read as a shadow. Anything else without a binding is reported as an error rather than guessed at.
Notes:
- The prefix is applied before the name is validated, so short names that are not legal CSS identifiers on their own (
2xs,0) become valid once prefixed. - The same prefix is applied when components are synced, so
var(--…)in generated component styles always matches the variables in the token files. - Groups sharing an
outputare merged into one file per theme, with a comment header per group. Two groups resolving to the same variable name in the same file is reported as an error. - Changing a prefix regenerates the files even though the Figma file itself did not change.
Typography variables
By default sync typography writes each style as a rule with literal values, and components repeat those values inline — the same number lives in two places and drifts when the design changes.
With config.typography.variables: true, every property becomes a custom property and the rule points at it:
:root {
--h1-bold-family: Inter;
--h1-bold-weight: 700;
--h1-bold-size: 72px;
}
.h1-bold {
font-family: var(--h1-bold-family);
font-weight: var(--h1-bold-weight);
font-size: var(--h1-bold-size);
}Names are --<style>-<property>, with the redundant font-/text- prefix dropped: family, weight, size, style, line-height, letter-spacing, decoration, transform.
sync component then references the same variables instead of literals — font-size: var(--h1-bold-size) — by matching the component's Figma text style (styles.text) against the typography set. That works on any plan: text styles, unlike variables, are part of the ordinary file response.
A property the style doesn't define never becomes a variable, and components keep its literal value, so a component can't reference a variable the generated file never wrote. Components fall back to literals entirely when typography isn't configured or its file is unreachable.
How component paths resolve
pathis used as-is (relative to where you runsnps); it is not prefixed bypaths.components.typesPathandstructurePathfall back to the component's ownpathwhen omitted — so by default the mixin, types and structure doc all land in the same folder.- A component's
figmaFilefalls back to the globalconfig.components.figmaFile.
What sync component generates
For a component named button, snps sync component button writes:
button.scss(orbutton.css) — a@mixin/ class ruleset generated from the Figma layout, with values resolved to CSS custom properties (var(--token-name)) via your synced tokens.button.types.ts— a TypeScript union of the component's Figma variant properties (enabled bytypes: true).button.md— a Markdown snapshot of the component (enabled bystructure: true), containing:- Description — the component description as entered in Figma;
- Variants — the variant API (each property and its possible values);
- Structure — the element hierarchy, annotated with the variant states under which each element appears.
The .md doc is meant as machine-readable context for an AI agent generating or refactoring the component to match the current design.
The component registry (add component)
snps add component treats a repository as a shadcn-style registry: it reads a registry.json from the configured repo, resolves the requested component plus its dependencies, and downloads each file into the matching directory.
// config.components.repo — defaults to the public Synapse components repo
"components": {
"repo": "https://raw.githubusercontent.com/<owner>/<repo>/main"
}snps add component button # button + its dependencies (icon, ...)
snps add component --all # every component in the registry
snps add component button -f # overwrite existing filesEach registry.json entry declares its files and dependencies, so pulling one component automatically brings in everything it imports.
Authentication
snps login runs the Figma OAuth flow and stores the resulting token under figma.token in synapse.config.json. All sync commands read from there.
Because the token is written into
synapse.config.json, keep that file out of version control (or stripfigma.token) if the repo is public.
License
MIT
