Skip to content

What gets emitted

specs react and specs webcomponents each emit a target whole. A component is not a file — it is a small set of files that only make sense together, and these pages describe them one at a time.

The set

FilePageWhat it is
contract.tsContractThe props interface, the enums those props draw from, and a defaults constant. Shared by both targets.
scaffold.tsx / scaffold.tsScaffoldThe working component — markup, variant attributes, conditional slots, subcomponent calls.
styles.css / host.css + light.cssStylesThe stylesheet the scaffold imports: one rule per anatomy element, variant selectors, token var() references.
stories.tsx / stories.tsStoriesStorybook CSF — controls typed from the contract, a story per variant axis, a sticker sheet.
cssvars.css + modes.jsonCSS variablesLibrary-level custom properties the stylesheets resolve against. Written once, shared by both targets.

Two more files appear when a component earns them: metadata.ts (slot shapes and visibility rules) when the component has slots, and api.ts (tag, parts, element-tag map) for every Web Components root. Both are covered on the contract page, because they are the same job — declaring the surface — for a different consumer.

Where they land

The directory is named for the component, so the files inside it do not repeat the name.

react/
├── src/
│ ├── _runtime.ts shared helpers, one per tree
│ └── components/
│ └── Checkbox/
│ ├── contract.ts
│ ├── metadata.ts
│ ├── scaffold.tsx
│ ├── styles.css
│ ├── stories.tsx
│ └── Control/ a subcomponent, nested as its spec is
│ └── …
webcomponents/
└── src/
├── _runtime.ts
└── components/
└── Checkbox/
├── contract.ts
├── metadata.ts
├── api.ts
├── scaffold.ts
├── host.css
├── light.css
├── stories.ts
└── Control/
└── …
assets/
└── cssvars/
├── cssvars.css
└── modes.json

A subcomponent is emitted as completely as a component — its own contract, stylesheet and stories — in its own directory under the parent, following the same rule.

Regenerated, and what isn’t

Every file on this page opens with a Generated. Do not edit header, and every run overwrites it. Nothing here is a starting point you edit in place.

The seam is a sibling: an authored component.tsx (React) or component.ts (Web Components) is never written by a command, and when it exists it is what the stories import instead of the scaffold. That is where an implementation the spec cannot express belongs.

_runtime.ts is written once per tree rather than per component — prop-splitting and glyph-URL helpers the scaffolds import. It is generated too.

What decides the content

Read fromDecides
specs/<component>/api.yamlprops, enums, anatomy, roles, actions, subcomponents
specs/<component>/variants.yamlthe layout tree, per-variant styling, slot visibility
specs/<component>/examples.yamlinstance and slot-content examples, for stories
config/conventions/<platform>.yamlnaming and emission conventions for that target
config/conventions/specs.yamlstate classification, accessibility and value props

A component with no variants.yaml is skipped with a warning rather than emitted empty — slot visibility and variant attributes both come from variant data.

The scaffold page shows one component in both surfaces — the annotated Figma layers, and the anatomy block specs generate writes from them.

See Also

  • react and webcomponents — the commands, their options, and the free/Pro seams
  • Roles — what a role changes about the element emitted and the props declared