Design System Contracts

Spec reference

Token bindings

Styling is a set of bindings to design tokens — generated onto both surfaces, gated for integrity at build time, never handwritten.

Token bindings & substitutionGeneratedCurated#

Every visual decision is a binding from a CSS property to a DTCG token reference — Record<cssProperty, TokenRef> where a TokenRef is a brace-wrapped token path. The CSS Module and the canvas variable bindings are generated from these; there is no handwritten style layer to drift.

Substitution: a {propName} placeholder inside a token path expands over that enum prop’s values — {color.action.{variant}.background} produces one CSS rule per variant and one bound variable per canvas variant. Root parts support multi-axis substitution; nested parts one placeholder per reference.

TokenRef — pattern rendered from the schema
TokenRef = string matching /^\{[a-z0-9.{}-]+\}$/i
// e.g. "{color.action.primary.background}"
//      "{color.action.{variant}.background}"  ← substituted over an enum prop

The integrity gate: at generation time, every reference — after expansion — must resolve to a real token in tokens/. A binding to a nonexistent token fails the build with the exact contract path and missing token named. The contract and the token set cannot silently disagree (eval: refuse-unknown-token-reference).

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

  • malformed references — anything not brace-wrapped token-path shaped
  • a substituted reference naming a prop that is not a declared enum
  • more than one placeholder on a nested part’s reference
contracts/button.contract.json (excerpt · v1.5.0) — shipping contract, loaded at build time
{
  "anatomy": {
    "root": {
      "tokens": {
        "background-color": "{color.action.{variant}.background}",
        "color": "{color.action.{variant}.foreground}",
        "padding-inline": "{space.inset-x.{size}}",
        "padding-block": "{space.inset-y.{size}}",
        "border-radius": "{radius.control}",
        "gap": "{space.gap.control}",
        "font-family": "{font.control.family}",
        "font-weight": "{font.control.weight}",
        "font-size": "{font.control.size.{size}}"
      }
    }
  }
}

Tokens by propGeneratedCurated#

tokensByProp: { prop, map } — the value-level sibling of the substituted reference. A substituted ref can only carry bindings whose token names spell the axis value; real foreign vocabularies name tokens by scale step ({spacing.200} on large, {spacing.150} on small), so a binding that is a plain function of one enum axis needs a per-value map. Map values are overrides merged over the part’s base tokens; only the values that deviate appear, and refs are plain — no placeholders.

Multiple entries (v14): a part may carry an ordered array of entries — one per driving axis (Button: variant colors and size paddings; Text: the variant scale and the fontWeight map). The single-object spelling stays valid. When two entries on different props override the same channel, the later entry wins — mirroring the CSS source-order cascade the values were extracted from. Two entries may not claim the same channel for the same prop: a conflicting channel+prop pair is refused by name.

proprequiredstring

The driving enum prop, by canonical name.

maprequiredRecord<string, Record<string, TokenRef>>

enum value → (CSS property → plain TokenRef), merged over the base tokens.

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

  • the driving prop must be a declared enum; every map key one of its values
  • a placeholder inside a mapped ref — per-value maps are the substitution; a placeholder would be double substitution
  • a component-instance part — the child contract owns its styling
  • two entries claiming the same channel for the same prop (a conflicting channel+prop pair) — within tokensByProp or across tokensByProp/literalsByProp
contracts/token.contract.json (excerpt · v1.1.0) — shipping contract, loaded at build time
{
  "anatomy": {
    "root": {
      "tokensByProp": {
        "prop": "size",
        "map": {
          "sm": {
            "padding-inline": "{space.inset-y.sm}"
          },
          "lg": {
            "font-size": "{font.control.size.sm}",
            "padding-inline": "{space.inset-x.sm}",
            "padding-block": "{space.inset-y.sm}"
          }
        }
      }
    }
  }
}

Literal channelsGeneratedCurated#

literals and literalsByProp (v14) carry the styling facts a foreign system keeps as component-private literals — Polaris’s --pc-* pixel geometry (ProgressBar’s per-size track heights, Avatar’s per-size widths, Button’s transparent base background). A literal is only carried when it was resolved deterministically through the source’s own var() chain at promotion time, with provenance; it is never minted into a token — the value is honestly literal, and renaming it against a real token is the adopter’s call.

The grammar is bounded: px/rem/em/unitless numbers, hex and rgb()/rgba() colors, and the CSS keywords transparent/inherit/currentColor. The channel set is bounded too (geometry and paint channels — see the refusal list). literalsByProp is the per-enum-value form, an ordered array with exactly the tokensByProp entry semantics.

proprequiredstring

The driving enum prop, by canonical name.

maprequiredRecord<string, Record<string, string>>

enum value → (CSS property → bounded literal), merged over the base literals.

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

  • a channel outside the bounded literal-channel set (background/background-color, color, height/width/min-*, padding-block/-inline, gap, border-radius, border-width, font-size, line-height, letter-spacing)
  • a value outside the bounded grammar (no gradients, no calc(), no keywords beyond transparent/inherit/currentColor)
  • a channel carried as BOTH a token binding and a literal on the same part — ambiguous
  • a component-instance part — the child contract owns its styling
per-size literal track heights (schema v14 — the Polaris ProgressBar shape) — illustrative, schema-validated at build time
{
  "element": "div",
  "tokens": {
    "background-color": "{color.progress.track}"
  },
  "literalsByProp": [
    {
      "prop": "size",
      "map": {
        "small": {
          "height": "8px"
        },
        "medium": {
          "height": "16px"
        },
        "large": {
          "height": "32px"
        }
      }
    }
  ]
}

Declared factsGeneratedCurated#

declared and declaredStates (v15, the S4 channel lifts) carry the keyword/literal styling channels that have no token vocabularycursor, user-select, appearance, text-rendering, font-feature-settings, transitions, touch-action, the position: relative class, the A22 text channels (text-transform, text-decoration-line, text-align, text-overflow), font-family stacks, and the background sub-channels. Before v15 these were extension-block residue; now they are first-class facts: every code emitter renders them verbatim, and the canvas either draws them natively (text case, decoration, alignment, truncation, first font-family stack entry — the capability matrix’s draw verdicts) or declares them without drawing (the matrix §b annotation copy lands in the component description — declared-not-drawn, never silently dropped).

declared is Record<cssProperty, value> on any non-ref part; declaredStates is the per-state form (Record<state, Record<cssProperty, value>>) — cursor staying pointer on :disabled, text-decoration-line: underline on :hover. A declared cursor or position fact is authoritative: the emitters’ own button chrome (cursor: pointer, the :disabled not-allowed rule) yields to it. The computed-capture floor promotes uniform observed values (and full-coverage uniform state deltas) into these fields automatically — see extract/computed/.

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

  • a channel outside the DECLARED_CHANNELS registry — token/literal-vocabulary channels belong in tokens/literals
  • a value outside the channel’s bounded grammar (e.g. position admits only relative/static — absolute placement belongs to overlay/stylesWhen)
  • a channel carried as BOTH a token binding (or literal) and a declared fact on the same part — ambiguous
  • a state outside the contract’s declared states, or outside the state vocabulary
  • a component-instance or slot part — the child contract / consumer owns its styling
declared facts (schema v15 — the Polaris Button shape the computed floor promotes) — illustrative, schema-validated at build time
{
  "element": "button",
  "declared": {
    "cursor": "pointer",
    "user-select": "none",
    "text-rendering": "optimizelegibility"
  },
  "declaredStates": {
    "disabled": {
      "cursor": "pointer"
    }
  }
}