Design system
The visual contract.
A design system is not a folder of pictures and not a style guide. It is an agreement between design and engineering about what the interface is made of — the same kind of artefact as an OpenAPI document, and subject to the same rules. It is settled once, away from any particular screen and before any particular feature, so that no screen has to settle it again. Story mapping and example mapping consume it; they do not produce it.
Three properties, and one of them is usually missing
Most design systems are explicit and agreed. The third property is where they fail: tokens copied by hand into CSS are a second implementation, and they drift exactly the way a hand-written API stub drifts — silently, while continuing to look correct.
- 01
Explicit
Written down where both sides can point at it — a Figma library and a token file, not a habit and a screenshot.
- 02
Agreed
Design and engineering both reviewed it, and both can propose a change to it. A contract one side can edit alone is a preference.
- 03
Enforced
Something fails when it is breached: tokens generated rather than retyped, lint rules against raw values, contrast checked in the suite.
The foundations
Eight decisions taken once. Each one names what it forbids, because a contract that forbids nothing constrains nothing — and each one says what happens when it is breached. 3 of the eight are not enforced by anything yet, and are marked as such below.
- Agreed
Colour
What may a surface, a text or a border be?
- Fixes
- A palette of raw values, and — the part that matters — the semantic roles laid over it: surface, surface-raised, ink, ink-muted, brand, danger, success, focus. Screens reference roles; only the theme references raw values.
- Forbids
- A hex code in a component. If a colour is worth using twice it is worth a role, and if it is used once it is probably a mistake.
- Enforced by
- A lint rule rejecting raw colour literals outside the token layer, plus a contrast check per role pairing in the accessibility suite.
- Agreed
Typography
What sizes, weights and rhythms exist at all?
- Fixes
- A closed type scale with a line height and a tracking per step, and a role per step — display, title, body, caption, code. Two families, no more.
- Forbids
- An arbitrary font size. A one-off 17px heading is a fork of the scale, and it will be copied by the next screen that needs something "a bit bigger".
- Enforced by
- The scale ships as tokens; a lint rule rejects literal font sizes and line heights in component styles.
- Agreed
Spacing and layout
How far apart is anything allowed to be?
- Fixes
- A 4px base with a named step per multiple, a container width per breakpoint, and the breakpoints themselves. Density is a token too, so a compact table is a setting rather than a rewrite.
- Forbids
- Off-scale margins. Most visual inconsistency between two screens built by two people is spacing, not colour.
- Enforced by
- Spacing tokens only in component styles, checked by the same lint rule as colour and type.
- Agreed
Interaction states
What does every interactive thing do when touched?
- Fixes
- The full set each interactive component must answer for: rest, hover, focus-visible, active, disabled, loading, error, and — the ones most often forgotten — empty and read-only.
- Forbids
- Shipping a component with only its happy state designed. An undesigned state is not absent; it is designed badly by whoever implements it under deadline.
- Enforced by
- A story per state, so a missing state is a missing story and shows up as a gap in the library rather than as a bug in production.
- Agreed
Accessibility
What is guaranteed for someone not using a mouse or a screen?
- Fixes
- Minimum contrast per role pairing, a visible focus ring that survives every theme, a target size floor, the heading and landmark structure of a page, and the reduced-motion behaviour of every animation.
- Forbids
- Removing an outline without replacing it, colour as the only carrier of meaning, and a control that a keyboard cannot reach.
- Enforced by
- An automated scan per story and per journey, failing the build — plus the manual keyboard pass that automation cannot do.
- Drafting
Motion
How does the interface move, and when does it not?
- Fixes
- Durations, easings and a small set of named transitions — enter, exit, expand, attention — plus what each one is for.
- Forbids
- Motion that is not attached to a state change, and any animation that ignores `prefers-reduced-motion`.
- Enforced by
- Nothing yet.Agreed and written down, but no build fails when it is breached — so today it is a convention, not a contract.
- Drafting
Iconography
What may a symbol look like, and what may it mean?
- Fixes
- One grid, one stroke weight, one optical size per context, and a name-to-meaning register so the same idea is never drawn two ways.
- Forbids
- An icon with no accessible name, and an icon-only button whose meaning is not also available as text.
- Enforced by
- Nothing yet.Agreed and written down, but no build fails when it is breached — so today it is a convention, not a contract.
- Planned
Content and voice
How does the product talk, especially when something has failed?
- Fixes
- Sentence case, the second person, dates and numbers per locale, and the shape of an error message: what happened, why, and what to do next.
- Forbids
- An error the user cannot act on. "Something went wrong" is a shrug with a stack trace behind it.
- Enforced by
- Nothing yet.Agreed and written down, but no build fails when it is breached — so today it is a convention, not a contract.
Where it actually lives
The Figma library is the source; the published tokens are the same decisions in a form a build can consume. One direction of flow — a hand-edit downstream is a bug in the source, not a local fix. When a session finds something none of this covers, that is a change request against the contract, decided here rather than inside a story.