Design System Contract 1.0
An open specification, JSON Schema and conformance checker for machine-readable component contracts. A contract is one JSON file per component that says what the component accepts, which usages are wrong, as rules a machine can evaluate, and which accessibility claims are proven, by what and when.
- Version
- 1.0, published 25 September 2026
- Editor
- Petri Lahdelma, Digitaltableteur
- Checker
- @digitaltableteur/contract-spec (external link) 1.0.0
- Schema
- contract.schema.json
- Licence
- Text CC BY 4.0; schema and code Apache-2.0
Check a design system
Point the checker at a folder of contracts. It reports the level of every contract and of the system as a whole, where the system level is the lowest level of any stable contract.
npx @digitaltableteur/contract-spec path/to/contractsThis site’s own contracts
The Digitaltableteur design system publishes its contracts at /contracts/v1/ and checks them with the same tool. Checked on 2026-11-04 at commit 856512eb5, with evidence older than 180 days counted as stale.
- Contracts
- 197
- Stable
- 108
- Accessibility claims re-proven by a machine
- 525
- Declared gaps
- 286
The system is at Level 3 of 3, Evidenced. 110 contracts reach Level 3; the rest stop at Level 1 because they do not yet record an owner and a last review date, which Level 2 requires. The gaps are listed, not hidden: that is the point of the format.
The specification
1. Why this exists
Design systems describe their components in documentation written for people. Coding agents now read that documentation too, and so do linters, migration tools and procurement reviews. Three problems follow:
- Prose cannot be checked. "Don't use an icon-only button without a label" is advice. Nothing can reject code that ignores it.
- Claims cannot be told apart. "Accessible" proven by a browser on every commit and "accessible" asserted once in a review last spring are the same sentence on the page.
- Nothing expires. A component changes; its documentation, test results and review notes silently stop describing it.
A contract is one JSON document per component that fixes all three: the public API, the usage rules as data a machine can evaluate, and every accessibility claim labelled with how it is verified, backed by records that say when and against which commit the verification ran.
The principle is simple: nothing an agent or a reviewer reads about a component is hand-asserted without saying so. A claim is either proven by a record anyone can re-check, dated by the person who made it, or declared as a gap.
2. Conventions
The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in all capitals.
A contract is a JSON document conforming to the schema. A system is a
set of contracts published together. A checker is software that
evaluates contracts against this specification; contract-check is the
reference checker.
3. The contract document
A contract MUST be a JSON object that validates against the 1.0 schema. The schema is normative for structure; this section states intent.
| Field | Required | Meaning |
|---|---|---|
specVersion | yes | Always "1.0" for this version. |
name | yes | The name consumers import. Dotted names (Tabs.Trigger) address compound parts. |
status | yes | experimental, alpha, beta, stable or deprecated. |
description | yes | What the component is for, in one or two sentences. |
props | yes | The public props API. May be empty. |
package | no | The package name and the exact import statement to write. |
rules | no | Machine-checkable usage rules (section 4). |
accessibility | no | Accessibility criteria (section 5). Required in practice for Level 2. |
governance | no | Who owns the contract and when a person last read it end to end. |
deprecation | when deprecated | Why, and the migration path. |
source | no | Repository paths of the implementation, for commit-based freshness. |
Fields beginning with x- are extensions. Checkers MUST ignore extensions
they do not understand. Any other unknown field is a schema error, so that a
misspelt field fails loudly instead of being silently ignored.
3.1 Props
Each prop MUST declare a type. A prop whose type is union MUST list its
allowed literal values; a union without values tells an agent nothing it can
check. A prop MAY declare optional, default and description.
A deprecated prop MUST carry a deprecated object with a reason. To reach
Level 2 it MUST also name a replacement (a prop a codemod can target) or a
removeIn version (a date a release gate can hold). A deprecation without a
migration path is prose.
3.2 Status
stable is a promise to consumers. A system's conformance level is computed
over its stable contracts (section 7), so a system cannot claim a level by
keeping its weakest components out of stable, and cannot reach one while a
stable component falls short.
4. Usage rules
A rule states a condition on one usage of the component and what must or must not be present when it holds.
{ "id": "icon-only-needs-name", "when": { "icon": { "present": true }, "children": { "absent": true } }, "requireAnyOf": ["accessibleName", "aria-label"], "reason": "An icon-only button has no accessible name.", "severity": "error"}whenmaps prop names to exactly one matcher:present,absent,equals(a literal) oroneOf(a list of literals). The rule fires when every matcher holds.childrenis a pseudo-prop for element children.- When a rule fires, a checker MUST report each
forbidprop that is present, MUST reportrequireAnyOfwhen none of its props is present, and MUST report eachrequireAllOfprop that is absent. equalsandoneOfMUST NOT match a value the checker cannot state as a literal (for example an expression it does not evaluate). A rule must never fire on something the checker cannot prove.severityiserrororwarning.evidenceMAY say where the rule comes from, for example a runtime warning in the component source.
Rules that cannot be expressed this way stay in documentation. The grammar is deliberately small: it covers the rules that break builds and accessibility most often (missing names, mutually exclusive props, props that only make sense together) and nothing a checker would have to guess about.
5. Accessibility criteria
accessibility.criteria is a list of statements about the component, each
with a verification mode:
verification | Meaning | Required fields |
|---|---|---|
automated | A machine re-proves it; records are attached. | check, evidence |
manual | A person checked it on a known date. | reviewedAt |
unverified | A declared gap. Not a claim. | none |
checkis the command that re-proves an automated criterion.wcagMAY list the WCAG 2.x success criteria the check exercises. Listing a success criterion is not a claim that it is met: automated checks can test part of a criterion, never prove conformance on their own.- Publishers SHOULD list known gaps as
unverifiedrather than omit them. An absent criterion reads as "not applicable"; anunverifiedone reads as what it is.
6. Evidence
An evidence reference points at a machine-written record of one run:
{ "artifact": "evidence/button-default.axe.json", "sourceCommit": "4f2a9c1e7b3d5a6c8e0f1a2b3c4d5e6f7a8b9c0d", "capturedAt": "2026-09-20T08:30:00.000Z", "passed": true, "runner": "ci"}artifactis a path or anhttpsURL. Relative paths resolve against the evidence root, which a checker MUST let the user set and SHOULD default to the directory containing the contract.sourceCommitis the commit the run checked.capturedAtis when it ran.- A checker MUST treat evidence as invalid when:
passedis nottrue;capturedAtis older than the freshness window or in the future; a local artifact does not exist; or a JSON artifact records a commit (sourceCommitorsourceSHA) or time (capturedAtorgeneratedAt) that disagrees with the reference. A contract cannot claim a fresher run than its record shows. - The default freshness window is 180 days. A checker MUST let the user change it.
- A checker MAY also offer commit-based freshness: when the contract lists
sourcepaths, evidence is stale if any of those paths changed aftersourceCommit. This is the stronger test; time windows are the portable floor.
7. Conformance levels
A contract meets the highest level whose requirements it and every lower level satisfy.
| Level | Name | Requirements |
|---|---|---|
| 1 | Described | Validates against the schema. |
| 2 | Checkable | Level 1, plus governance with an owner and a review date, at least one accessibility criterion, and every deprecation carries a migration path. |
| 3 | Evidenced | Level 2, plus at least one automated criterion; every automated criterion has valid, fresh evidence (section 6); every manual criterion was reviewed within the freshness window. |
A system's level is the lowest level met by any of its stable
contracts. A system with no stable contracts is measured over its
non-deprecated contracts. A checker SHOULD report the distribution of levels
and the count of automated, manual and unverified criteria next to the system
level, because a single number hides where the gaps are.
A publisher claiming conformance SHOULD state the level, the specification version, the checker and its version, the freshness window, and the date of the check, for example: "Design System Contract 1.0, Level 3, contract-check 1.0.0, 180-day window, checked 2026-09-25."
8. Relationship to other standards
- W3C Design Tokens (DTCG). Tokens describe values; contracts describe components. A contract MAY reference token names in extensions; this specification does not define token formats.
- WCAG 2.2 and EN 301 549. Criteria map to WCAG success criteria for orientation only (section 5). EN 301 549 v4 adopts WCAG 2.2 and numbers web requirements as clause 9 plus the WCAG number, so the mapping carries over.
- Model Context Protocol. A design-system MCP server can serve contracts and evaluate section 4 rules for an agent before its code is written. This specification defines the data; it does not define tools.
9. Versioning
This specification follows semantic versioning. A minor version MAY add optional fields and reporting detail; it MUST NOT make a valid 1.0 contract invalid or lower its level. Anything else is a major version.
10. Reference implementation
npx @digitaltableteur/contract-spec path/to/contracts --level 2The Digitaltableteur design system publishes its own contracts in this format
at public/contracts/v1/ in its repository and checks them with the same
tool on every push, including the levels it does not yet meet.
Why contracts matter once AI agents write your UI is argued in Design systems for bots (external link) on petrilahdelma.com.
Stable contracts
The 108 stable contracts and the level each one reaches. Every name opens the contract itself.
| Component | Level | Automated | Manual | Gaps |
|---|---|---|---|---|
| AboutPageContent | 3 Evidenced | 3 | 1 | 1 |
| AlertBanner | 3 Evidenced | 3 | 1 | 2 |
| ArticleContent | 3 Evidenced | 3 | 1 | 1 |
| ArticleShareSection | 3 Evidenced | 3 | 1 | 2 |
| Author | 3 Evidenced | 3 | 1 | 0 |
| AuthorBio | 3 Evidenced | 3 | 1 | 1 |
| Avatar | 3 Evidenced | 3 | 1 | 1 |
| Badge | 3 Evidenced | 3 | 1 | 1 |
| BlogCategoryFilter | 3 Evidenced | 3 | 1 | 2 |
| BlogMediaImage | 3 Evidenced | 3 | 1 | 1 |
| BlogNav | 3 Evidenced | 3 | 1 | 1 |
| Breadcrumb | 3 Evidenced | 3 | 1 | 2 |
| Button | 3 Evidenced | 1 | 2 | 2 |
| Card | 3 Evidenced | 2 | 1 | 2 |
| CategoryFilter | 3 Evidenced | 3 | 1 | 2 |
| Checkbox | 3 Evidenced | 3 | 1 | 2 |
| CheckboxGroup | 3 Evidenced | 3 | 1 | 2 |
| ClientLogoMarquee | 3 Evidenced | 3 | 1 | 1 |
| Combobox | 3 Evidenced | 3 | 1 | 2 |
| ContactFormEditorial | 3 Evidenced | 3 | 1 | 2 |
| ContactHero | 3 Evidenced | 3 | 1 | 0 |
| ContactInquiryPanel | 3 Evidenced | 3 | 1 | 2 |
| ContactPageContentEditorial | 3 Evidenced | 3 | 1 | 1 |
| Container | 3 Evidenced | 3 | 1 | 1 |
| CTASection | 3 Evidenced | 3 | 1 | 1 |
| DesignSprintsSection | 3 Evidenced | 3 | 1 | 1 |
| Divider | 3 Evidenced | 3 | 1 | 1 |
| DonnyAvatar | 3 Evidenced | 3 | 1 | 1 |
| EmptyState | 3 Evidenced | 3 | 1 | 2 |
| EnhancedProjectCard | 3 Evidenced | 3 | 1 | 2 |
| ExpandableSection | 3 Evidenced | 3 | 2 | 2 |
| FileUpload | 3 Evidenced | 3 | 1 | 2 |
| FilterChip | 3 Evidenced | 3 | 1 | 2 |
| FormField | 3 Evidenced | 3 | 1 | 2 |
| FormFieldEditorial | 3 Evidenced | 3 | 1 | 1 |
| Gallery | 3 Evidenced | 3 | 2 | 2 |
| Grid | 3 Evidenced | 3 | 1 | 0 |
| GridBlock | 3 Evidenced | 3 | 1 | 0 |
| GroupLabel | 3 Evidenced | 3 | 1 | 1 |
| HelperText | 3 Evidenced | 3 | 1 | 1 |
| HeroSection | 3 Evidenced | 3 | 1 | 0 |
| HighlightSection | 3 Evidenced | 3 | 1 | 1 |
| HomeHero | 3 Evidenced | 3 | 1 | 2 |
| Icon | 3 Evidenced | 2 | 1 | 1 |
| IconButton | 3 Evidenced | 3 | 1 | 2 |
| Kbd | 3 Evidenced | 3 | 1 | 1 |
| Label | 3 Evidenced | 3 | 1 | 1 |
| LanguageSwitcher | 3 Evidenced | 3 | 1 | 2 |
| Link | 3 Evidenced | 3 | 1 | 2 |
| List | 3 Evidenced | 3 | 1 | 1 |
| ListItem | 3 Evidenced | 3 | 1 | 1 |
| Logo | 3 Evidenced | 3 | 1 | 1 |
| MarkdownMessage | 3 Evidenced | 3 | 1 | 2 |
| Menu | 3 Evidenced | 3 | 1 | 2 |
| Modal | 3 Evidenced | 3 | 1 | 2 |
| MultiCombobox | 3 Evidenced | 3 | 1 | 2 |
| NavLink | 3 Evidenced | 3 | 1 | 2 |
| NewsBulletin | 3 Evidenced | 3 | 1 | 2 |
| PageLayout | 3 Evidenced | 3 | 1 | 0 |
| Pagination | 3 Evidenced | 3 | 1 | 2 |
| PhoneInput | 3 Evidenced | 3 | 1 | 2 |
| PricingCalculator | 3 Evidenced | 3 | 1 | 2 |
| PricingPageContent | 3 Evidenced | 3 | 1 | 2 |
| ProcessBlock | 3 Evidenced | 3 | 1 | 1 |
| Progress | 3 Evidenced | 3 | 1 | 1 |
| ProjectCard | 3 Evidenced | 3 | 2 | 1 |
| ProjectGallery | 3 Evidenced | 3 | 2 | 2 |
| ProjectNav | 3 Evidenced | 3 | 1 | 1 |
| ProofBlock | 3 Evidenced | 3 | 1 | 1 |
| ReadingProgress | 3 Evidenced | 3 | 1 | 1 |
| Section | 3 Evidenced | 3 | 1 | 0 |
| SecureCVDownload | 3 Evidenced | 3 | 1 | 2 |
| Select | 3 Evidenced | 3 | 1 | 2 |
| SelectableCard | 3 Evidenced | 3 | 1 | 2 |
| SelectOption | 3 Evidenced | 3 | 1 | 2 |
| ServiceCard | 3 Evidenced | 3 | 1 | 1 |
| ServicesBlock | 3 Evidenced | 3 | 1 | 1 |
| ServicesSection | 3 Evidenced | 3 | 1 | 1 |
| SiteFooter | 3 Evidenced | 3 | 1 | 2 |
| SiteHeader | 3 Evidenced | 3 | 1 | 2 |
| SiteTree | 3 Evidenced | 3 | 1 | 2 |
| Skeleton | 3 Evidenced | 3 | 1 | 1 |
| SkipLink | 3 Evidenced | 3 | 1 | 2 |
| SlideButton | 3 Evidenced | 3 | 2 | 2 |
| SocialShare | 3 Evidenced | 3 | 1 | 2 |
| Spinner | 3 Evidenced | 3 | 1 | 1 |
| Stack | 3 Evidenced | 3 | 1 | 1 |
| StatusDot | 3 Evidenced | 3 | 1 | 1 |
| StoryBlock | 3 Evidenced | 3 | 1 | 1 |
| Switch | 3 Evidenced | 3 | 1 | 2 |
| Table | 3 Evidenced | 3 | 1 | 2 |
| TableCell | 3 Evidenced | 3 | 1 | 1 |
| TableHeaderCell | 3 Evidenced | 3 | 1 | 2 |
| TableRow | 3 Evidenced | 3 | 1 | 1 |
| Tabs | 3 Evidenced | 3 | 1 | 2 |
| TeamBlock | 3 Evidenced | 3 | 1 | 1 |
| Text | 3 Evidenced | 1 | 2 | 1 |
| TextArea | 3 Evidenced | 3 | 1 | 2 |
| TextInput | 3 Evidenced | 3 | 1 | 2 |
| Timestamp | 3 Evidenced | 3 | 1 | 1 |
| Title | 3 Evidenced | 1 | 2 | 1 |
| Toast | 3 Evidenced | 3 | 1 | 1 |
| ToastStack | 3 Evidenced | 3 | 1 | 1 |
| ValueCard | 3 Evidenced | 3 | 1 | 1 |
| VisuallyHidden | 3 Evidenced | 3 | 1 | 0 |
| WorkMagneticField | 3 Evidenced | 3 | 1 | 1 |
| WorkNav | 3 Evidenced | 3 | 1 | 1 |
| WorkPreviewSection | 3 Evidenced | 3 | 1 | 1 |