Spec reference
Composition — slots, refs, repeat
Slots with checkable constraints, fixed instances by reference, and item templates over structured props — composition that generation and parity can both verify.
Slots — constrained insertion pointsGeneratedCurated#
A slot is a constrained insertion point, aligned with the canvas’s two-tier constraint design. A slot named children is the default slot (React children); any other name becomes a ReactNode prop. accepts lists contract IDs, resolved per surface through each referenced contract’s anchors — a declared slot without a checkable constraint would leave generation and parity nothing to verify, so the constraint is first-class.
children = the default slot; any other name becomes a ReactNode prop of that name.
Contract IDs this slot accepts. Omit = unconstrained.
prefer (default): accepts guides pickers and generators. restrict: only accepts is legal. open: explicitly anything — the escape hatch. Compatibility rule: widening is a minor version; narrowing is major.
Arity lower bound (maps to canvas slot min-children).
Arity upper bound.
The slot must be filled.
Canvas property name. Default: PascalCase(name).
See default content.
{
"anatomy": {
"root": {
"parts": {
"endArea": {
"parts": {
"endContent": {
"description": "End-aligned action slot — typically a secondary Button.",
"slot": {
"name": "endContent",
"accepts": [
"ds.button"
],
"acceptsMode": "prefer"
},
"optional": true,
"layout": {
"direction": "row",
"align": "center"
}
}
}
}
}
}
}
}Slot default contentGeneratedCurated#
Design-time sample content: renders as instances inside the slot on the canvas and as the sample in code stories — never baked into the generated component itself. Items must be drawn from accepts when accepts is present. A slot whose default content has multiple items is a multi-child slot — inexpressible as a canvas instance-swap, so it renders its content directly until the native slot-property migration.
A contract id from accepts.
Fixed prop values, spelled canonically.
Overrides the child’s children text prop.
Refusals: Each of these fails the build by name — the generator refuses, it never papers over (source: core/emit-react.ts validateContract, exercised by the C2 eval family).
- default content outside
accepts(eval:refuse-defaultContent-outside-accepts) - unknown contract references; composition cycles — a contract cannot compose itself
texton a child with no children text prop
{
"anatomy": {
"root": {
"parts": {
"stack": {
"layout": {
"direction": "row",
"align": "center",
"overlap": true
},
"tokens": {
"gap": "{space.avatarGroup.overlap}"
},
"slot": {
"name": "children",
"accepts": [
"ds.avatar"
],
"acceptsMode": "prefer",
"defaultContent": [
{
"id": "ds.avatar",
"props": {
"size": "md"
},
"text": "AB"
},
{
"id": "ds.avatar",
"props": {
"size": "md"
},
"text": "CD"
},
{
"id": "ds.avatar",
"props": {
"size": "md"
},
"text": "EF"
}
]
}
}
}
}
}
}Component refs — fixed instancesGeneratedCurated#
A fixed instance of another contract, embedded by reference — composition never duplicates a child’s definition. Props are spelled canonically and mapped through the child contract’s own bindings on each surface. A string value of the form "{parentProp}" maps the parent’s enum prop into the child per variant.
The child contract’s id, e.g. ds.avatar.
Fixed prop values; "{parentProp}" threads a parent enum through.
Overrides the child’s children text prop (code: JSX children; canvas: text override on the instance).
Per-instance channel overrides — see below.
Refusals: Each of these fails the build by name — the generator refuses, it never papers over (source: core/emit-react.ts validateContract, exercised by the C2 eval family).
- unknown child contracts; cycles (
a contract cannot compose itself) - setting an unknown child prop, or an
arrayOfchild prop — structured values cannot be fixed in anatomy - a
"{parentProp}"reference to a prop that is not a declared enum of the parent texton a child with no children text prop
{
"anatomy": {
"root": {
"parts": {
"header": {
"parts": {
"avatar": {
"description": "Fixed identity anchor — an instance of ds.avatar.",
"component": {
"id": "ds.avatar",
"props": {
"size": "sm"
}
}
}
}
}
}
}
}
}Per-instance overridesGeneratedCurated#
component.overrides carries observed per-occurrence facts of this usage that diverge from what the child contract renders on its own — a Figma instance can carry its own image fill, its own box, its own solid paint or glyph ink, and the child contract cannot know them. Keys come from the REF_OVERRIDE_CHANNELS registry (below); values are token refs minted from the observed per-occurrence values, never invented, and axis-substitutable. Field case: the Avatar host drawing its glyph stub’s silhouette in its own ink and box.
Consent is two-sided. The child must list each channel in its root part’s overridable — the channels its own styling consumes through the override custom property (--<child-id>-<channel>, derived from the child contract id so nesting different children never crosses channels) with its own bindings as the var() fallback. Absent an override, behavior is value-identical to the plain bindings. overridable is declared at propose time from the same observed evidence that minted the underlying channels — it grants exactly this: which registered channels a host may set on an instance of this contract.
| Channel | CSS it overrides | The observed Figma fact |
|---|---|---|
background-image | background-image | per-instance IMAGE fill identity (a Figma instance carries its own imageHash — dump v1.9 imageFill observed on the instance node). |
size | width, height | per-instance box (a Figma instance is freely resizable — observed bbox, SQUARE boxes only: one custom property drives width and height, and any nested part binding the same refs, e.g. a stub glyph). |
background-color | background-color | per-instance solid paint (dump v1.7 instancePrimaryFill, fill-shaped) — stub roots declare it (their paint IS the observed instance paint); a real child owns its own paint. |
color | color | per-instance GLYPH INK (gap-closing round 8; dump v1.7 instancePrimaryFill, stroke- or fill-shaped, observed on the nested instance node). The twin of background-color for a vector child: an exported glyph whose whole drawing is ONE ink draws `currentColor`, its own contract binds that ink as `color`, and a host that draws the same glyph in its own ink sets this channel. A glyph with two or more distinct inks is refused by the single-ink test (examples/untitled-ui/glyph-ink.mts) and never reaches this channel — one custom property cannot honestly serve two paints. |
Refusals: Each of these fails the build by name — the generator refuses, it never papers over (source: core/emit-react.ts validateContract, exercised by the C2 eval family).
- an override channel outside the registry
- overriding a channel the child’s root does not declare
overridable— the child’s CSS would never consume the custom property, and a silent no-op is refused by name overridableon a non-root part — only the root consumes per-instance overrides- an
overridablechannel whose CSS properties the root binds no token for — nothing to consume the override through
Honest carriage: the react-css-modules surface carries overrides as custom properties set by a structural wrapper, consumed by the child’s own var() fallback chains. The canvas emitter ledgers overrides as channelMiss (declared-not-drawn) this round — carried and named, not yet drawn.
{
"id": "imported.user",
"overrides": {
"size": "{imported.avatar.user.size.{size}}",
"color": "{imported.avatar.user-instance.color}"
}
}Repeat — item templates over arraysGeneratedCurated#
Repeated-children collections (menu items, breadcrumb segments, tab items, avatar stacks): the part is an item template — a component-ref part rendered once per record of the itemsProp arrayOf prop. Field → child-prop mapping is by name: every arrayOf field names a prop of the referenced child contract. Constant child props ride component.props as usual.
Projections: React maps the live array ({items?.map(…)} — undefined renders nothing); the static surfaces and the canvas render sample — the observed drawn siblings, the collection’s honest static state (the meter discipline again).
The arrayOf prop (by canonical name) the template maps over in code.
The observed design-time sample — one record per drawn sibling, keys ⊆ the arrayOf fields. Required: the canvas projection is the sample; a sample-less collection would render nothing everywhere but React.
Refusals: Each of these fails the build by name — the generator refuses, it never papers over (source: core/emit-react.ts validateContract, exercised by the C2 eval family).
repeatwithoutcomponent— the item template is a component ref- a repeat template carrying slot/content/text/meter alongside
itemsPropunknown, or not anarrayOfprop- a field colliding with a fixed component prop — a field is per-item, a fixed prop is constant
- a field naming no child prop; sample keys outside the fields; sample values of the wrong type
{
"linkNeutral": {
"component": {
"id": "ds.link-neutral",
"props": {
"iconLeft": false,
"underlined": "false",
"size": "large",
"state": "default"
}
},
"repeat": {
"itemsProp": "items",
"sample": [
{
"iconRight": false
},
{
"iconRight": true
},
{
"iconRight": true
},
{
"iconRight": false
},
{
"iconRight": false
}
]
}
}
}