Design System Contracts

An open specification for component contracts

The source of truth is neither the design file nor the code.

A design system's truth should live in a machine-readable contract that sits between the surfaces and generates both. This is the working spec for that contract — vendor-neutral, deterministic, and open — with a reference implementation that proves every claim it makes.

New here and not sure what this does? Get started is organised by what you want: a component on the canvas that you want as code (A), components in code that you want in Figma (B), or a mature library on both sides that you want reconciled (C).

Four positions, held together

A growing category of tools speaks this vocabulary; this project holds four positions that, together, none of them do.

Bidirectional

The contract generates both the code and the design canvas, and imports from both — round-trips are proven, not promised.

Deterministic

Every artifact is computed from file data and byte-pinned; no LLM guesses in the pipeline (AI is available as an assistant, never as an authority).

Receipted

Anything the pipeline cannot carry is named on screen — a gap is reported, never papered over with a plausible value.

Open

The schema, the engine, and every instrument that verifies them are in one repository under one permissive license, with no gated tier — because a spec the community can't fully use isn't a spec.

The model

Every organization that takes design systems seriously eventually splits into two camps. Some come in from the code side: the system is an npm package, and the design files are an aging picture of it. Others come in from the design side: the system is a canvas library, and the code is an approximation of the pictures. Both camps are answering the same question — where does the truth live? — and both answers fail the same way: whichever surface is declared canonical, the other becomes a hand-maintained copy. Copies drift. Drift erodes trust.

Workflow diagram: the contract sits between the design surface and the code surface. Generation flows outward from the contract to both surfaces; changes on either surface flow back into the contract as promotions, and the contract regenerates the other side. A three-way differ verifies all of it continuously. Surfaces never sync side-to-side.Workflow diagram: the contract sits between the design surface and the code surface. Generation flows outward from the contract to both surfaces; changes on either surface flow back into the contract as promotions, and the contract regenerates the other side. A three-way differ verifies all of it continuously. Surfaces never sync side-to-side.

The rule that makes it work: surfaces never sync side-to-side. An engineer's new prop and a designer's color change take the same path — flagged by the differ, promoted into the contract as a reviewable diff, then regenerated out to the other surface. One arbiter, version-controlled, no arbitration meetings. It's the governance model that made Git work for code and the DTCG token format work for design tokens, run one level up — at the component-API layer.

How it works — three questions, answered with the engine running →

What a contract looks like

One small, versioned JSON document per component: props and their legal values, anatomy, token bindings, slot constraints, accessibility semantics, declared events. This excerpt is a shipping contract from the reference implementation — the same file generates the typed React component and the canvas component set, and a three-way differ proves both keep matching it.

contracts/banner.contract.json (excerpt · v1.0.0) — shipping contract, loaded at build time
{
  "id": "ds.banner",
  "name": "Banner",
  "version": "1.0.0",
  "props": [
    {
      "name": "status",
      "description": "Feedback tone — drives the color scheme, the leading icon, and the ARIA role (error/warning announce as alerts).",
      "type": {
        "enum": [
          "info",
          "success",
          "warning",
          "error"
        ]
      },
      "default": "info",
      "bindings": {
        "figma": {
          "kind": "VARIANT",
          "property": "Status",
          "values": {
            "info": "Info",
            "success": "Success",
            "warning": "Warning",
            "error": "Error"
          }
        },
        "code": {
          "prop": "status"
        }
      }
    }
  ],
  "anatomy": {
    "root": {
      "tokens": {
        "max-width": "{size.banner.width}",
        "background-color": "{color.feedback.{status}.background}",
        "color": "{color.feedback.{status}.foreground}",
        "border-radius": "{radius.banner.{container}}",
        "gap": "{space.gap.control}",
        "padding-inline": "{space.inset-x.md}",
        "padding-block": "{space.inset-y.lg}"
      }
    }
  }
}

One file; two faithful renderings; a differ that can mechanically prove both. Every field is specified in the reference — generated from the schema itself, so the docs cannot drift from the spec.

The proof, counted

51
component contracts
in contracts/
282
DTCG design tokens,
one pipeline, both surfaces
188/188
deterministic evals green
(npm run eval)
4
emitters behind one interface:
react · html · react-inline · figma
154/154
schema branches documented —
the coverage receipt
1,618/1,618
enterprise kit sets imported clean
(the census)

Counted from the repository at build time — contracts, tokens, and the emitter registry are read, not quoted; instrument numbers come from the committed reports they cite.

What it does, and what that costs — the pair

Those counts are about this repository. The question an adopter actually asks is what the tool does to their library, and that has two halves which are only honest together. What it does: 54 components from 6 third-party libraries score 89.6% mean computed-style equality against the original package rendering (exact string match, no tolerance, 379,861 cells), and in the other direction a Figma kit converts to code at 92.70% over 537 scored variants. What that costs: those 54 components are 6.0% of the 893 in the libraries they came from, and they were picked because they were the tractable ones.

Three doors in