Transforms
The transform command fans a component specification out into artifacts your codebase can consume: a TypeScript contract, a baseline stylesheet, a working component, a token inventory. Instead of writing those by hand, you derive them from the spec and keep them in sync as the design evolves. This concept is described in RFC 001: Component Dictionary.
Transforms take spec data as far as it deterministically goes — every prop, token, and style Figma captured — before inference enters the picture. Structured spec data is stable, regeneratable, and cheap to re-read; it’s the right foundation for agents and tooling to build on, not a replacement for them. What the spec can’t know — behavior, interaction states, accessibility semantics — belongs to agentically-extended specs and the authored files that live alongside.
Output
output/ _dictionary/ styling.byComponent.json # token usage indexed by component (specs analyze) styling.byToken.json # token usage indexed by token name across components
dsAlert/ api.yaml # spec variants.yaml # variant data generated/ DsAlert.contract.ts # from specs transform contract DsAlert.styles.css # from specs transform css react/ DsAlert.scaffold.tsx # from specs transform react — always current, do not edit DsAlert.stories.tsx # from specs transform stories src/ react/ DsAlert.tsx # seeded once by react transformer, then human-owned DsAlert.extensions.css # authored — styling the spec can't express DsAlert.proposed.css # authored — styling proposed for promotion into the specEvery transform output file — generated and authored — is PascalCase-prefixed with the component name (DsAlert.*), even though the folder itself already scopes it to that component; this keeps filenames self-describing when opened outside the tree (an editor tab, a diff view, a search result). All generated output for a component lives under its generated/ subfolder, keeping it clearly separated from the source api.yaml/variants.yaml and from anything you author. The react transformer is the one exception that writes outside generated/: it seeds a one-time authored copy under src/react/, described in Authored vs. Generated below.
Authored vs. Generated
contract, css, react, and stories all regenerate their generated/ output on every run — never edit those files directly, since the next specs transform overwrites them.
The react transformer additionally seeds src/react/{Component}.tsx plus .extensions.css and .proposed.css the first time it runs for a component. Those three files are created once and never touched again, even on subsequent runs — they’re yours to implement against. The stories transformer imports this authored component, not the regenerated scaffold, so Storybook always reflects what you’ve built.
How It Works
Prerequisites
specs transform discovers components by scanning the output directory for subfolders that each contain an api.yaml. That exact shape — a per-component subfolder with api.yaml and variants.yaml inside it — only comes from running generate with both --split-components and --split-concerns:
specs generate --split-components --split-concernsIf you only see a single library.yaml, or {Component}.yaml files with no api.yaml inside, re-run generate with both flags above before transforming.
Processing
specs transform discovers component subfolders under the output directory (each must contain an api.yaml), then runs one or more named transformers against every component.
specs transform [transformers...] [options]Transformer names can be passed as positional arguments, configured in specs.config.yaml, or left absent to use the CLI default (contract).
Resolution Order
- Positional arguments —
specs transform css react config.transform.transformersinspecs.config.yaml- CLI default:
contract
Types
| Transformer | Output file | What it produces |
|---|---|---|
contract | generated/{Component}.contract.ts | TypeScript Props interface and defaults constant, plus Slots/SlotRules when variants.yaml is present |
css | generated/{Component}.styles.css | CSS rules per anatomy element, with token vars, variant selectors, and structural presence/stacking fixes |
react | generated/react/{Component}.scaffold.tsx + seeded src/react/{Component}.tsx | A working React component wired to the contract and stylesheet, seeded once into an authored file you own |
stories | generated/react/{Component}.stories.tsx | A Storybook CSF page with a story per prop-expressible variant, importing the authored component |
react and stories both require variants.yaml — components without it are skipped with a warning.
Filtering by Component
By default specs transform runs against every component subfolder in the output directory. Use --components to scope a run to specific components:
specs transform react stories --components dsAlert dsBadgeRunning All Transformers
specs transform contract css react storiesOr configure them in specs.config.yaml so specs transform alone is enough:
config: transformers: - name: contract - name: css - name: react - name: storiesreact and stories both assume contract and css have already produced generated/{Component}.contract.ts and generated/{Component}.styles.css for the component — list them in this order.
See Also
transformcommand — full CLI reference- transform config — configure default transformers