Design System Contracts

Spec reference

Props & bindings

The canonical API: names, five type kinds, defaults — and the bindings that render one prop onto two surfaces.

The canonical APICurated#

Each prop declares its canonical name, type, and default — and bindings, which describe how the one canonical prop manifests on each side. The canonical value set lives here and only here: canvas spelling ("Primary") and code spelling ("primary") are renderings of the canonical value.

Prop fieldsGeneratedCurated#

namerequiredstring

Canonical prop name — the spelling the contract owns.

descriptionstring

Flows into JSDoc and Storybook autodocs.

typerequired"boolean" | "text" | "number" | { enum: string[] } | { arrayOf: Record<string, "text" | "number" | "boolean"> }

See types.

defaultstring | boolean | number

Must match the type — enum defaults must be members, boolean defaults booleans, and so on (refused by name otherwise). Required text props must still declare a string default: it is the canvas default and the story sample.

requiredboolean

Text props may be required (no default in the code signature).

bindingsrequired{ figma: { kind: "VARIANT" | "BOOLEAN" | "TEXT" | "INSTANCE_SWAP" | "NONE"; property?: string; values?: Record<string, string> }; code: { prop: string } }

See bindings.

TypesGeneratedCurated#

Five kinds, rendered from the schema union:

KindCode surfaceCanvas surface
"boolean"native attribute where the element supports it (disabled on <button>), otherwise a data-* attributeBOOLEAN property
"text"children or a string propTEXT property
"number"number propTEXT property (stringified)
{ enum: [...] }typed union prop; one CSS class per valueVARIANT axis; every enum prop becomes an axis (full cartesian, defaults-first)
{ arrayOf: {...} }items?: Array<{ … }> — an optional array; undefined means “not provided”, never a silent []none — code-only by declared fidelity limit (see below)
prop.type — rendered from the schema union at build time
type: "boolean" | "text" | "number" | { enum: string[] } | { arrayOf: Record<string, "text" | "number" | "boolean"> }

Declared fidelity limit — structured props. The canvas has no list-of-records property type, so an arrayOf prop must bind figma.kind: "NONE" and every design-side consumer skips it rather than reporting it behind. Both directions are enforced: arrayOf ⇔ kind "NONE".

arrayOf prop proposed at build time by the import engine (core/propose-figma) from the committed owner’s-kit fixture extract/figma/gauntlet/fixtures/pattern-repeat-collection-navigation-header.dump.json
{
  "name": "items",
  "type": {
    "arrayOf": {
      "iconRight": "boolean"
    }
  },
  "bindings": {
    "figma": {
      "kind": "NONE"
    },
    "code": {
      "prop": "items"
    }
  }
}

Bindings — one prop, two manifestationsGeneratedCurated#

figmarequired{ kind: "VARIANT" | "BOOLEAN" | "TEXT" | "INSTANCE_SWAP" | "NONE"; property?: string; values?: Record<string, string> }

How the prop appears on the canvas: property kind, property name, and the canonical-value → variant-value spelling map.

coderequired{ prop: string }

The React prop name.

prop.bindings — rendered from the schema at build time
figma: { kind: "VARIANT" | "BOOLEAN" | "TEXT" | "INSTANCE_SWAP" | "NONE"; property?: string; values?: Record<string, string> }
code:  { prop: string }

Refusal rules on props and bindings: 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).

  • duplicate prop names; duplicate code bindings across props, slots, and events (the git-merge attack)
  • two props binding the same design property — the canvas cannot host both
  • an enum default outside the enum; type-mismatched defaults
  • a figma values map missing an enum value, or carrying a key that is not one
  • kind: "NONE" on a prop that is neither arrayOf nor text — every other scalar prop has a canvas manifestation (a text prop may be code-only: a label the canvas carries as a raw per-instance character override, which has no component property to bind; it must declare a string default, since that default is what the canvas draws)
  • bindings.figma.property required unless kind is "NONE" — and refused when it is
contracts/button.contract.json (excerpt · v1.5.0) — shipping contract, loaded at build time
{
  "props": [
    {
      "name": "variant",
      "description": "Visual prominence of the action.",
      "type": {
        "enum": [
          "primary",
          "secondary",
          "danger",
          "ghost"
        ]
      },
      "default": "primary",
      "bindings": {
        "figma": {
          "kind": "VARIANT",
          "property": "Variant",
          "values": {
            "primary": "Primary",
            "secondary": "Secondary",
            "danger": "Danger",
            "ghost": "Ghost"
          }
        },
        "code": {
          "prop": "variant"
        }
      }
    }
  ]
}