Spec reference
The contract document
Identity, lifecycle, anchors, and the top level of the document — the shape everything else hangs from.
Top-level fieldsGeneratedCurated#
A contract is one JSON document per component — contracts/<component>.contract.json. It captures everything design and engineering must agree on; both libraries are renderers of it. Types below are rendered from the schema; follow the links for the fields that have their own pages.
Editor affordance: points at the emitted JSON Schema (contracts/contract.schema.json, npm run schema) so contracts validate inline as you type.
Stable canonical identity — never renamed. The namespace before the dot is the owning system’s (ds.button here, acme.chip in a brownfield extraction). Display names may change on either side; the id is what survives.
Display / export name (Button). Drives the code export and the canvas component-set name. Must be PascalCase — it becomes file names.
Semver, the unit of change management. Added optional prop = minor; removed or renamed prop or value = major. See versioning.
"draft")Governance lifecycle: draft → stable → deprecated.
Usage intent — one sentence, flowing into Storybook autodocs and the canvas component description. The same sentence on both surfaces, from one source.
The HTML element, ARIA role, and prop-driven variants of both — see Semantics & accessibility.
The canonical API — see Props & bindings.
The declared interaction surface — see Events & toggles.
[])Interaction states the component must support — see States.
component (default) generates a canvas component set. native declares the concept maps to a native canvas capability (layout primitives are auto-layout): no canvas component is generated, parity does not expect one, and the code surface is still fully generated and checked.
Receipt-grade metadata naming the token modes a drawn theme/mode variant axis carried (e.g. ["light","dark"]). The axis is never a prop — theming lives in the token collection’s modes — so this field changes no emitter output; it names the fact for reviewers and round-trip tooling.
Opt-in canvas-only state previews — see States.
The named part tree where all styling decisions live — see Anatomy & parts.
Executable accessibility requirements — see below.
Per-side identity anchors — see below.
Anchors — rename-safe identityGeneratedCurated#
The DTCG $extensions dual-ID pattern applied to components. After the first generation on each side, that side’s stable identifiers are written back here (npm run anchors:writeback). From then on, renames on either side never fork identity — parity matches by anchor, not by name.
The design-tool side: file key, component-set key, node id. null until first sync — the differ reports such contracts as pending, which is workflow state, not drift.
The code side: import path and export name of the generated component.
{
"id": "ds.button",
"name": "Button",
"version": "1.5.0",
"status": "draft",
"anchors": {
"figma": {
"fileKey": "8nim1d0IPnehMxA7B7SYxC",
"componentSetKey": "1b5d2a573f3f39404af396bdbe944a30ca0eaec3",
"nodeId": "5:21"
},
"code": {
"importPath": "src/components/Button",
"export": "Button"
}
}
}a11y — declared requirementsGeneratedCurated#
The component must have a visible focus treatment (drives the focus-visible state rules).
Minimum hit area in px.
Target WCAG contrast tier.
Honesty note: the a11y block is declarative-only today — minHitArea and contrast are recorded but no generator or differ enforces them yet. The roadmap names the choice: enforce or remove. (This caveat is stated in docs/07 — Validation, and this page repeats it rather than hiding it.)