Design System

Do & don't

The rules that are not open to interpretation. Everything here is enforced at review: a change that breaks one of these is a defect, not a style preference.

What is this?

A single page collecting the system's hard constraints, so a contributor or a composing team can check their work against one list.

When to use it

  • Before opening a change that touches the design system
  • When reviewing a screen composed from the system
  • When you are tempted to make an exception

How to use it

  • Read the rule, then the do/don't pairs for the area you are working in
  • Route new values through the token layer instead of the call site
  • Ask for a system change rather than a local override

What to avoid

  • Local overrides that duplicate an existing variant
  • One-off values that only look right on one screen
  • Shipping an exception without documenting it here

Rule 1 — Values come from tokens

No raw visual values in components or screens

Colour, spacing, typography, radius, elevation, motion and icon sizes are all governed by tokens. A hex value, an rgb(), an arbitrary Tailwind value such as p-[13px], or a raw pixel font size is a defect.

If a value genuinely does not exist yet, add it to tokens.css, map it in theme.css, document it on the relevant foundation page, and then use it.

Rule 2 — Lucide React only

One icon library, five sizes

All icons come from lucide-react. No custom SVGs, no second library, no emoji substitutes. Every icon is sized with size-(--size-icon-xs | sm | md | lg | xl).

Icon-only controls are rendered with IconButton and wrapped in a Tooltip whenever the action is not universally obvious.

Rule 3 — Soft Glass is for floating surfaces only

ds-glass-soft never touches page content

The Soft Glass treatment — translucent surface plus backdrop blur — is permitted on dialogs, drawers, popovers, dropdown and context menus, the command menu, and toasts. These surfaces float above content and can be dismissed.

It is forbidden on anything in the document flow: page headers, sidebars, cards, table rows, sticky toolbars, banners. Blur on in-flow content harms text rendering and scrolling performance, and destroys the layering cue that makes floating surfaces read as temporary.

Rule 4 — DateRangePicker always shows a label

The label prop is required

DateRangePicker renders a visible label above its trigger by default, exactly like every other form control in the system. The trigger alone — showing only a formatted range — is not an acceptable form field: it gives keyboard and screen-reader users no name, and sighted users no indication of what the range applies to.

Use hideLabel only when an adjacent visible label already names the control; the name is still exposed to assistive technology. Pass optional to mark it non-required, and error to surface validation, which is announced and associated with the trigger.

DateRangePicker labelling API.
PropTypeBehaviour
labelstring (required)Visible label above the trigger and the accessible name
hideLabelbooleanVisually hides the label; the accessible name is preserved
optionalbooleanMarks the field optional in the label row
errorstringAnnounced validation message associated with the trigger

Rule 5 — One primary action per view

Hierarchy is a prop, not a decision at the call site

Each screen has exactly one primary button. Secondary, tertiary and ghost variants carry everything else, and destructive actions are separated from routine ones and confirmed with an AlertDialog.

Quick reference

Do

  • Use semantic tokens (bg-surface, text-text-secondary, border-border-default).
  • Compose existing components before writing a new one.
  • Give every field a visible label and every chart a data-table alternative.
  • Cover populated, loading, empty and error states on every data screen.
  • Keep status colour for meaning and brand colour for actions.

Don't

  • Do not add hex values, arbitrary spacing, or raw font sizes.
  • Do not nest cards, or wrap a whole page in a surface.
  • Do not apply backdrop blur to page chrome.
  • Do not ship a DateRangePicker without a label.
  • Do not introduce a boolean styling prop where a variant belongs.