Skip to main content

Design system: from decision to component

A design system is a shared language between product, design, content and code. It speeds up repeated decisions and makes exceptions visible. It is not a gallery of screenshots or a collection of isolated components.

Four levels

1. Principles

Principles filter decisions: clarity before decoration, visible progress, one primary action per zone, accessibility in the structure and explicit feedback after an action.

2. Tokens

A token names a reusable decision: accent color, surface, text, space, radius, shadow, control height or motion duration.

:root {
--sl-accent: #00d1ff;
--sl-text-primary: #ffffff;
--sl-text-secondary: rgba(255, 255, 255, 0.72);
--sl-space-4: 16px;
--sl-radius: 12px;
--sl-control-height: 44px;
}

A semantic name survives a visual change better than --blue-3. Tokens should not become an unreadable layer of indirection: each needs a documented intent and place of use.

3. Primitives

A primitive carries a low-level contract: Button, Input, Badge, Card, ProgressBar, Alert, Modal or Tabs. It uses the right HTML, exposes states and accepts only necessary properties.

4. Patterns

A pattern assembles primitives for a recurring problem: sign-in form, filtered list, review session, error summary or resume flow. It documents the product decision and must not hide different rules behind one name.

Specify a component

For every component, write a short sheet:

QuestionExample for ProgressBar
What problem?Show progress and remaining work.
What role?Progress with exposed value and maximum.
Which variants?Determinate or indeterminate, according to the contract.
Which states?Idle, updating, complete, error if the product needs it.
What content?“Card 3 of 10”, not a context-free bar.
Which constraints?Readable at zoom, keyboard and screen reader.
How to test?Zero, maximum, dynamic change and assistive technology.

A variant should exist only for a real intent, priority level or observable state. Button color="purple" describes appearance; variant="primary" describes responsibility.

States are part of the contract

Ask:

  • how does idle look?
  • how are focus and keyboard described?
  • what happens during waiting?
  • what change confirms success?
  • how does an error explain recovery?
  • what appears when there are no results?
  • why is the action disabled, and what can be done instead?

The component can provide mechanics and style; the page owns context-specific copy. A generic alert must not replace an error that names the field and fix.

Accessibility in the contract

Design accessibility with the primitive: native button before clickable div, visible label before placeholder, aria-describedby for help and error, readable focus, aria-live only for changes worth announcing, touch sizing, responsive reflow and reduced motion.

A page should not have to repair a shared component with five ARIA attributes and a different keyboard handler. If it does, the component contract is incomplete.

Documentation and governance

Useful documentation shows when to use a component, when not to use it, a minimal example, an error example, allowed properties, keyboard and responsive states, content and accessibility decisions, owner, version and change path.

Add a primitive only when the problem is frequent and stable enough for a shared contract. Otherwise, a local composition may be more honest.

Evolve without breaking

  • Add a variant only with a real use case and a test.
  • Prefer a documented migration to a silent change of meaning.
  • Mark a primitive deprecated and name its replacement.
  • Check consumers before renaming a token.
  • Measure usage or build errors after a major change.
  • Keep English rules and copy aligned with French.

Workshop: specify a review card

Write the component sheet before its CSS: role and goal, question and answer, progress, reveal and answer actions, in-progress/completed/error/empty states, control relationships, keyboard/mobile/long-copy behaviour and evidence that the card is complete.

One possible answer

The card is an <article> named by its heading. The question is a heading, progress is readable text, the answer is connected to the button with aria-controls and visibility is exposed with aria-expanded. “In progress”, “completed” and “error” include text. Actions stay in flow, work by keyboard and stack on mobile. The card is complete when a correct answer and progress evidence are saved.

System checklist

  • Every token has a known intent and usage.
  • Every component has a native role, name and documented states.
  • Variants correspond to decisions, not free colors.
  • Errors, empty states and loading are represented.
  • Focus, keyboard, zoom, responsive layout and motion are tested.
  • Examples include long content and a translation.
  • Contribution and retirement paths are clear.

Reliable sources

Continue with CSS for an interface, then test the system in the interactive UI/UX path.