Today we're releasing version 0.1 of our Design System Contract, an open JSON format for writing a design system's rules down as data, so that AI coding agents can read them instead of guessing.
It's live now:
- Specification and schemas: github.com/knapsack-oss/design-system-contract
- Reference docs: knapsack-oss.github.io/design-system-contract
- License: Apache-2.0. Use it, implement it, fork it. No Knapsack account required.
v0.1 is small on purpose. It's two file formats, their JSON Schemas, and worked examples.
The aim is to ship something small and correct, then grow it with the people who use it.
Here's what's in it, and why we built it.
What's in v0.1
A component contract describes one component. It gives the component a stable ID, keeps the design system's own display name, and lists its props. Each prop has a type, and can have a default, a description, and a list of allowed values. Slots and states are optional.
{
"contractId": "button-primary",
"component": "Button Primary",
"props": {
"properties": {
"label": { "type": "string" },
"variant": { "type": "string", "enum": ["primary", "secondary"], "default": "primary" }
},
"required": ["label"],
"additionalProperties": false
}
}
A manifest lists a design system's contracts, with each file's location and a content hash. When a contract changes, its hash changes, so a tool can tell whether it's reading the version the manifest describes.
Any JSON Schema validator can check both files. There's nothing else to install.
Why we built it
AI coding agents write a lot of product UI now. To use one of my favorite terms, it's often "plausible but wrong". Decent on first glance, badly drifted from your design system under the hood.
An agent passes a size prop your button doesn't have. It invents a tertiary variant. It builds a new card from scratch, three files away from the one your team already built.
The agent is guessing. The rules it needs live in a docs site, a Figma library, or a component package's source, and none of those tell it plainly what's allowed. So it works from the code in front of it. That code shows what someone did once. It doesn't show what the rules are. Even with context, an agent can't tell which of two competing claims is the rule.
That isn't a model problem. It's an infrastructure problem.
Most design systems keep their rules in prose and precedent. That worked when everyone using the system was a person who could ask in Slack. An agent can't ask. So the system drifts, one plausible-looking pull request at a time.
A contract is how you stop the drift. The rules stop being something an agent has to infer, and become something it can read.
Three v0.1 decisions
Props are JSON Schema. The props block is itself a valid JSON Schema, so a tool can check a component's props against it with a standard validator. There are enough type systems in the world. We don't need to invent another one.
Variants are just props with allowed values. There's no separate variants section. A prop named variant with an enum is a variant axis, and so is size, and so is tone. That's how teams already write components, and it's how DSDS describes them. We're matching the work that exists.
IDs come from one fixed rule. Button Primary, Button_Primary, and button.primary all become button-primary, on every machine. Names that don't convert cleanly, like Café, get rejected instead of guessed at. If two components end up with the same ID, the spec requires publishing to fail. It doesn't quietly merge them.
Better to fail and let you know than drift by accident.
It fills a slot DSDS left open
The Design System Documentation Specification (DSDS) describes a design system's documentation. Its component entries have a specs field for pointing at machine-readable contracts, and DSDS leaves the format of those contracts open on purpose. A Design System Contract fills that slot:
{
"id": "button-primary",
"specs": [
{ "href": "./contracts/button-primary.contract.json", "rel": "contract", "role": "Design System Contract" }
]
}
Every contract ID is a valid DSDS ID, so the two line up one to one. The specification also shows how contract props map onto DSDS traits.
DSDS describes the documentation. The contract describes the rules.
What v0.1 leaves out
Shipping something small and correct meant cutting a lot from any first release.
- design tokens
- strictness rules that differ between prototype and production code
- an index file for agents
- provenance and signing
- a checker tool
That last one matters. A schema validator checks a contract's shape, but not the ID and collision rules above. Until there's a checker, those are rules on paper.
Each one has a short write-up in the repository's future-directions note. None of them is a promise. What goes into v0.2 depends on what you tell us you need.
Why open
A contract only works if both sides can read it.
If your linter, your coding agent, and your docs tooling each need our permission to read the format, it isn't much of a standard. So the specification and schemas are Apache-2.0, with the patent grant that license carries. No certification. No registration. No Knapsack account.
The rules that govern what AI builds for your organization shouldn't be locked inside any one vendor's tools. That includes ours.
Try it today
- Read the README and the example contract in the repository.
- Write a contract for one component from your own design system.
- Validate it against
schemas/component.contract.schema.jsonwith any JSON Schema 2020-12 validator.
It takes about five minutes.
Then tell us where it broke: a prop you couldn't describe, a name the ID rule mangled, a field you expected and didn't find. Open an issue in the repository. Proposals for new fields are welcome, especially ones that borrow from formats you already use.
v0.1 is the starting point, not the finished standard.
Your agents are already guessing. Give them a contract.

