Contracts · 01
What a component contract is
One file per component that sits above both Figma and code. Both are checked against it, so when they disagree, the argument is already settled.
Most design systems keep a component in two places: a component set in Figma and a component in code. They start out matching. Then someone adds a variant in Figma, someone else adds a prop in code, and a year later nobody can say which one is right.
A component contract is a third file that sits above both. It says what the component is, and Figma and code are both checked against it. When they disagree, the contract decides.
Figma
Component set
- Variantvariant
- Sizevariant
- Disabledboolean
- Labeltext
- Start iconinstance swap
- End iconinstance swap
Properties must match the props. Layers must match the parts.
button.contract.json
- Props
- variant4size3disabledchildrenstartIconendIcon
- States
- hoverfocus-visiblepresseddisabled
- Parts
- roothitAreastartIconlabelendIcon
- Tokens
- 45 component tokens, by part and state
- Access
- EnterSpaceTabfocus ring · 32px target
Code
@base-ui/react 1.9.0
- hover:hover
- focus-visible:focus-visible
- pressed:active
- disabled[data-disabled]
Every state needs a selector. Every part must exist in the installed library.
The button contract from Phantom, my own design system, read straight from the file when this page is built.
What goes in one
Props. Each one, with its values and how it shows up in Figma: a variant property, a boolean, a text field. The Figma component set has to have the same properties with the same values.
States. Hover, focus, pressed, disabled. Only the states on this list can be styled. If the CSS styles another one, the build fails.
Parts. The named pieces the component is built from, like the label and the icons. Each part says which token it reads for each property, in each state. That's where the 44 tokens come from.
Accessibility. The keyboard model, the focus ring and the minimum touch target. A component that can be clicked has to have all three, or the build fails.
The contract never names a library. That's what lets one file describe Figma and code at the same time.
Why not just use the code as the source?
Because code is one implementation, and Figma is another. A separate map says how each part is built in one library, and the checker reads the installed library to make sure those parts exist. A new platform is a new map, not a rewrite of every component.
It also gives AI tools something reliable to read. An assistant that can look up the contract knows which props exist and which tokens each part uses, so it stops guessing. I'll write about how the checker works in the next notes.