Design System Contracts

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.

namerequiredstring

children = the default slot; any other name becomes a ReactNode prop of that name.

acceptsstring[]

Contract IDs this slot accepts. Omit = unconstrained.

acceptsMode"prefer" | "restrict" | "open"

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.

minnumber

Arity lower bound (maps to canvas slot min-children).

maxnumber

Arity upper bound.

requiredboolean

The slot must be filled.

figmaPropertystring

Canvas property name. Default: PascalCase(name).

defaultContentSlotContentItem[]

See default content.

contracts/banner.contract.json (excerpt · v1.0.0) — shipping contract, loaded at build time
{
  "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.

idrequiredstring

A contract id from accepts.

propsRecord<string, string | boolean>

Fixed prop values, spelled canonically.

textstring

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
  • text on a child with no children text prop
contracts/avatar-group.contract.json (excerpt · v1.0.0) — shipping contract, loaded at build time
{
  "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.

idrequiredstring

The child contract’s id, e.g. ds.avatar.

propsRecord<string, string | boolean | { prop: string; map: Record<string, string> }>

Fixed prop values; "{parentProp}" threads a parent enum through.

textstring

Overrides the child’s children text prop (code: JSX children; canvas: text override on the instance).

overridesRecord<string, TokenRef>

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 arrayOf child prop — structured values cannot be fixed in anatomy
  • a "{parentProp}" reference to a prop that is not a declared enum of the parent
  • text on a child with no children text prop
contracts/card.contract.json (excerpt · v1.1.0) — shipping contract, loaded at build time
{
  "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.

ChannelCSS it overridesThe observed Figma fact
background-imagebackground-imageper-instance IMAGE fill identity (a Figma instance carries its own imageHash — dump v1.9 imageFill observed on the instance node).
sizewidth, heightper-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-colorbackground-colorper-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.
colorcolorper-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
  • overridable on a non-root part — only the root consumes per-instance overrides
  • an overridable channel 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.

the Avatar host’s glyph stub, drawn in the host’s observed ink and box (committed usage: examples/untitled-ui/storybook/contracts/avatar.contract.json) — illustrative, schema-validated at build time
{
  "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).

itemsProprequiredstring

The arrayOf prop (by canonical name) the template maps over in code.

samplerequiredArray<Record<string, string | boolean | number>>

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).

  • repeat without component — the item template is a component ref
  • a repeat template carrying slot/content/text/meter alongside
  • itemsProp unknown, or not an arrayOf prop
  • 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
proposed at build time by the import engine from the committed owner’s-kit capture — five drawn menu items collapse to one template with the varying boolean carried per item
{
  "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
        }
      ]
    }
  }
}