Skip to content

Design

Last verified:

View Markdown

Based on WCAG 2.2, the Apple Human Interface Guidelines, Material Design 3 and long-standing usability research (Nielsen’s heuristics). The project’s look (brand, colours, fonts, components) lives in its own docs, for example docs/design-system.md; these rules apply to every design.

  • Clarity first: each screen has one primary purpose and one primary action. Decoration must help the user understand or act.
  • Clear hierarchy through size, weight, spacing and position before colour. Users scan, so key information comes first.
  • Consistency: the same thing looks and behaves the same everywhere, and follows platform conventions users already know.
  • Show system status: every action gets visible feedback within about 100 ms, and progress for anything over about 1 s.
  • Prevent errors before reporting them: sensible defaults, constraints, confirmation for destructive actions, and undo where possible.
  • Recognition over recall: visible options, labels on icons, and remembered input.
  • Honest UI: label estimates and illustrations. Never imitate system dialogs, live data or official seals. No dark patterns, fake urgency or fake social proof.
  • Mobile first: design at 360 px, then use extra space on larger screens. Don’t add features there.
  • Use a spacing scale based on 4 or 8 px. Related items sit closer together than unrelated ones.
  • Layout with grid and flex plus gap. Use container queries for components that live in different widths, and media queries for page layout.
  • Content width is about 65–75 characters for reading. Avoid full-width text on large screens.
  • No fixed heights on text containers. Set min-width: 0 on flex and grid children that hold text. Use dvh/svh instead of vh for full-height mobile layouts, and respect env(safe-area-inset-*).
  • Use logical properties (margin-inline, padding-block) so right-to-left languages work.
  • Use at most two families (often one) and a few weights. Use system fonts or self-hosted variable fonts.
  • Use a modular type scale (ratio about 1.2–1.25) with fluid sizes via clamp(). Body text is at least 16 px with line height about 1.5. Headings use about 1.1–1.3.
  • Use text-wrap: balance for headings and text-wrap: pretty for paragraphs. Use tabular figures for numbers in tables.
  • Never justify body text or set long passages in all caps.
  • Define colour as semantic tokens (--color-surface, --color-text, --color-accent, --color-danger), not literal names (--blue-500). Prefer OKLCH for perceptually even palettes.
  • Use mostly neutral surfaces with one accent colour for interaction and emphasis. Status colours (success, warning, danger, info) mean only that.
  • Light and dark themes are token sets that follow prefers-color-scheme, with a manual toggle. Set color-scheme, and apply the stored theme before first paint.
  • In dark mode, use dark grey rather than pure black, show elevation with lighter surfaces, and soften saturated colours.
  • Never use colour as the only signal; add text, icons or patterns.
  • Define colours, type scale, spacing, radii, shadows, z-index and durations once as tokens. Components use tokens, never raw values.
  • Scope component styles (CSS modules, framework scoping or @layer). Avoid !important and deep selectors.
  • Use native elements before custom ones: <button>, <a>, <dialog>, popover, <details>, and form controls styled with accent-color where enough.
  • Every interactive component handles: default, hover, :focus-visible, active, disabled, loading, error, empty and success.
  • Buttons say what happens (“Save draft”, “Delete project”). There is one primary button per view; destructive buttons look destructive.
  • Icons only on their own when universally understood; otherwise add a text label. Icon-only buttons have an accessible name and a tooltip.
  • Loading: use skeletons that match the layout for content, spinners only for short actions, and optimistic updates where safe.
  • Empty states explain what belongs there and offer the next action. Error states say what happened and how to fix it, and keep the user’s input.
  • Toasts are for brief, non-critical confirmations. Errors that need action stay on the screen.
  • Use modals only for focused tasks or confirmation. Never auto-play sound or video with sound. Avoid carousels for core content.
  • Single column, with visible labels above fields. Placeholders are not labels. Mark optional fields rather than required ones when most are required.
  • Use the right type, inputmode and autocomplete on every field, so keyboards and autofill work.
  • Validate on blur and on submit, not on every keystroke. Show errors next to the field and in a summary for long forms, and move focus to the first error.
  • Never ask for the same information twice in one flow. Allow paste in password and code fields.
  • Motion explains change (where something came from, what changed). Keep UI transitions about 150–250 ms with ease-out, and animate only transform and opacity.
  • Wrap non-essential motion in prefers-reduced-motion: no-preference. Never shift layout, and nothing flashes more than 3 times per second.
  • Use semantic HTML first, and ARIA only when no native element fits. Pages have lang, landmarks, a skip link and descriptive headings that expose the real hierarchy. House rule: one h1 per page, with no skipped levels.
  • Everything works by keyboard in a logical order with a visible focus indicator. Focused elements are never hidden behind sticky headers or banners.
  • Dialogs trap focus, close on Escape and return focus to where it came from.
  • Contrast: 4.5:1 for text, 3:1 for large text, icons, focus indicators and control boundaries, in every theme and state.
  • Pages work at 200% zoom, with text spacing increased, and at 320 px width without horizontal scrolling.
  • Targets are at least 24×24 CSS px or have enough spacing (WCAG 2.5.8, with its exceptions for inline links). Aim for 44×44 on touch. Any drag action has a single-pointer alternative.
  • Meaningful images have alt text and decorative ones have empty alt. Video has captions, and audio has transcripts.
  • Sign-in allows password managers and paste, with no cognitive tests such as puzzles. Help and contact links stay in a consistent place.
  • Announce async results and status messages with a live region.
  • Run automated axe checks in end-to-end tests with zero serious or critical issues. Do a keyboard-only pass and a screen-reader spot-check before release.