open-design/docs/design-systems.md
NotLeaped84 4c15ea45fa
docs: add design system authoring guide (#961)
* fix(i18n): add loom and trading-terminal to FR/RU summaries only

* docs: add design system authoring guide

* fix: remove loom/trading-terminal from locale (missing dirs), fix docs contradictions
2026-05-09 15:34:11 +08:00

12 KiB
Raw Blame History

Design System Authoring Guide

Parent: spec.md · Siblings: architecture.md · skills-protocol.md · agent-adapters.md

This guide covers everything a contributor needs to submit a design system that passes review the first time. If you are adding a design system to design-systems/<slug>/DESIGN.md, read this document before opening a PR.


1. The 9-Section Schema

Every DESIGN.md must have these nine section headings:

## 1. Visual Theme & Atmosphere
## 2. Color
## 3. Typography
## 4. Spacing
## 5. Layout & Composition
## 6. Components
## 7. Motion & Interaction
## 8. Voice & Brand
## 9. Anti-patterns

The schema parser extracts headings with ## [0-9].* — it matches the section number prefix, not the full text. You can add context after the number (e.g., ## 4. Spacing & Grid or ## 4. Spacing and layout). Only the ## [digit]. prefix is required. Empty section bodies are acceptable (for rarely-used tokens like motion), but the nine numbered headings must be present.

Header format

The first H1 becomes the label shown in the design-system picker dropdown. The > Category: line immediately after the H1 determines grouping:

# Design System Inspired by YourBrand

> Category: Developer Tools
> One-line summary for the picker preview.

Available categories: AI & LLM, Developer Tools, Productivity & SaaS, Backend & Data, Design & Creative, Fintech & Crypto, E-Commerce & Retail, Media & Consumer, Automotive, Editorial & Print, Retro & Nostalgic, Bold & Expressive, Modern & Minimal, Professional & Corporate. If none fit, introduce a new one with a PR comment explaining why.


2. The Review Framework: Lens A and Lens B

All design system PRs are reviewed against two lenses. Understanding these before you submit eliminates most round-trips.

Lens A — Code Correctness (P1/P2)

Is the file structurally valid and machine-processable? Failing Lens A is blocking.

Checks:

  • All 9 section headings present and in order
  • Color tokens are real hex codes (#RRGGBB or #RGB), not #REPLACE_ME, currentColor, or CSS variable names
  • No duplicate folder names in design-systems/
  • CSS variables wrapped in :root {} blocks (not bare in the document)
  • Font labels for catalog extraction present (see Section 3 below)
  • prefers-reduced-motion targets specific elements, not a global * selector
  • Dark mode tokens use [data-theme="dark"] override pattern, not duplicate token blocks

Lens B — Reasoning Completeness (P3)

Is the content substantive and useful, not just syntactically valid? Failing Lens B generates a P3 comment, not a hard block.

Checks:

  • Color palette lists all roles used in the design system, not just primary/secondary
  • Type scale includes Display, H1, Body, Caption (minimum 4 tiers)
  • Components section has real CSS, not Lorem Ipsum or placeholder /* TODO */ blocks
  • Anti-patterns are specific (e.g., "Do not use rounded corners > 4px" rather than "Avoid bad design")
  • Dark mode is a genuine override with different token values, not a copy of the light block
  • Prior art section names real, specific products or design systems (not "inspired by good design")

3. CSS Variable Structure

:root block (required)

All CSS variables must be inside a :root {} block. Bare CSS variable declarations at the top level of a section are invalid.

/* Correct */
:root {
  --color-primary: #625DF5;
  --color-bg: #FFFFFF;
  --font-sans: "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
}

/* Incorrect — not valid standalone CSS outside :root */
--color-primary: #625DF5;

Every color, spacing, typography, and shadow token belongs in :root. The exception is component-scoped overrides (e.g., .card { --card-padding: 16px; }) which belong under Components.

Dark mode pattern

Use [data-theme="dark"] to override tokens for dark mode:

:root {
  --color-primary: #625DF5;
  --color-bg: #FFFFFF;
}

[data-theme="dark"] {
  --color-primary: #7B75FF;
  --color-bg: #0D0D0D;
}

Do not create separate CSS blocks for light and dark without using the [data-theme="dark"] selector — it breaks the semantic token system.

Font labels for catalog extraction

Include this block in the Typography section for the daemon's parser regexes:

Font labels for catalog extraction:

Display: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif
Body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif
Mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace

Labels must be Display:, Body:, Mono: with a colon, followed by the full font stack. The daemon reads these to populate the design-system catalog.


4. Accessibility Requirements

WCAG AA contrast ratios

All text and data colors must pass 4.5:1 minimum contrast ratio against their background (4.5:1 for normal text, 3:1 for large text at 18px+ or 14px+ bold).

How to verify:

  • Use a contrast checker tool (e.g., WebAIM Contrast Checker, or #B37A00 on #FFFFFF)
  • Test each foreground token against its paired background token — not against white by default

Common mistakes:

  • Claiming WCAG compliance without testing — the review will catch this
  • Using a color that "looks fine" on white but fails on the actual dark surface
  • Warning/caution colors like #B37A00 (3.05:1 on white, 5.35:1 on #FFF3CD) — verify against the correct background context

Tertiary text tokens (timestamps, metadata, grid labels) on dark surfaces must still pass 4.5:1. Do not use #4A6080 on #0A0A0A — that is 2.1:1. Use #808086 on #0A0A0A instead (4.54:1).

Focus states

Every interactive component (buttons, links, input fields, cards with click handlers) must have a :focus-visible style:

.button-primary:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

This is a Lens A accessibility requirement. Keyboard-only users get no visual feedback without it.


5. Component Section Best Practices

The Components section is the most commonly rejected part of a design system. Common failures:

Do not use hardcoded colors in component CSS. Every color must reference a semantic token:

/* Correct */
.button-primary {
  background: var(--color-primary);
  color: var(--color-text);
}

/* Incorrect — hardcoded white breaks dark mode */
.button-primary {
  background: var(--color-primary);
  color: #ffffff;
}

Use semantic names for states. Prefer --color-state-success over #00D26A directly in component CSS.

Example component structure:

/* Status Badge */
.badge {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  border-radius: 2px;
}

.badge-success {
  background: rgba(38, 222, 129, 0.15);
  color: var(--color-success);
  border: 1px solid rgba(38, 222, 129, 0.3);
}

.badge-warning {
  background: rgba(255, 159, 67, 0.15);
  color: var(--color-warning);
  border: 1px solid rgba(255, 159, 67, 0.3);
}

.badge-critical {
  background: rgba(255, 71, 87, 0.15);
  color: var(--color-critical);
  border: 1px solid rgba(255, 71, 87, 0.3);
}

6. Motion & Interaction

prefers-reduced-motion

Target specific properties, not all elements globally:

/* Correct — targets only the elements that animate */
@keyframes pulse-glow {
  0%, 100% { text-shadow: 0 0 8px currentColor; }
  50% { text-shadow: 0 0 20px currentColor; }
}

@media (prefers-reduced-motion: reduce) {
  .alert-banner { animation-duration: 0.01ms !important; }
  .countdown { animation: none; }
}

/* Incorrect — global * selector disables transitions everywhere */
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; }
}

Timing conventions

--transition-fast:   100ms ease-in;
--transition-base:   150ms ease-out;
--transition-slow:   300ms ease-out;

Match easing to purpose: ease-in for entering, ease-out for leaving, linear for continuous motion (scrolling, data updates).


7. Locale Coverage Requirements

When adding a new design system, you must also add its localized catalog entry. This is a separate PR (do not mix design system and i18n changes).

Which locales need updating?

Use this decision tree to decide which array to add to:

Does a localized summary already exist for this design system?

  • Yes → Add to *_DESIGN_SYSTEM_SUMMARIES only (FR + RU). Never also add to the fallback arraybuildLocalizedContentIds() concatenates both without deduplication, producing a duplicate that fails the test.
  • No (no translation yet) → Add to *_DESIGN_SYSTEM_IDS_WITH_EN_FALLBACK only. This makes the system appear in the catalog with English fallbacks.
Locale File to update Array
German apps/web/src/i18n/content.ts DE_DESIGN_SYSTEM_IDS_WITH_EN_FALLBACK
French apps/web/src/i18n/content.fr.ts FR_DESIGN_SYSTEM_SUMMARIES (if localized copy exists) or FR_DESIGN_SYSTEM_IDS_WITH_EN_FALLBACK (if not)
Russian apps/web/src/i18n/content.ru.ts RU_DESIGN_SYSTEM_SUMMARIES (if localized copy exists) or RU_DESIGN_SYSTEM_IDS_WITH_EN_FALLBACK (if not)

⚠️ The duplicate-ID trap: buildLocalizedContentIds() concatenates summary keys and fallback IDs without deduplication. Adding to both arrays produces the same ID twice, and the e2e test's curated ID list rejects duplicates. This is exactly what caused the loom/trading-terminal #929 failure.

Test behavior

The e2e/tests/localized-content.test.ts test verifies that every design-systems/*/DESIGN.md on disk has a corresponding catalog entry. If your design system directory does not exist in the locale PR's branch, do not add it to the DE fallback array — the test builds the expected ID list from the filesystem.


8. Anti-patterns Section

The anti-patterns section is where reviewers check if you understand what your design system is not. Good anti-patterns are specific and bounded:

## 9. Anti-patterns

- Do not use decorative colors in data displays — every hue must convey operational meaning
- Do not use rounded corners greater than 4px — this aesthetic is functional, not friendly
- Do not use proportional fonts for telemetry values — monospace exclusively for data
- Do not animate non-alert elements — motion is reserved for signals that matter
- Do not use light mode — low-light environments are the only context

Bad anti-patterns are vague:

  • "Avoid bad design"
  • "Don't overcomplicate things"
  • "Use good colors"

9. Pre-submission Checklist

Before opening a PR, verify:

  • All 9 section headings present in order
  • No #REPLACE_ME or placeholder hex codes
  • All CSS variables wrapped in :root {}
  • Font labels block present (Display / Body / Mono)
  • [data-theme="dark"] block overrides light tokens, not copies them
  • Interactive components have :focus-visible styles
  • All color tokens verified at 4.5:1+ contrast against their paired background
  • No hardcoded colors (like #ffffff) in component CSS — use semantic tokens
  • prefers-reduced-motion targets specific elements, not *
  • Anti-patterns are specific and bounded, not vague prose
  • Dark mode section explicitly states the design intent if dark-only
  • No duplicate CSS block definitions (check for .panel appearing twice)
  • Category choice confirmed against existing category list

10. Design System Size Guide

A well-documented design system is typically 300600 lines. Being too brief (under 100 lines) triggers a Lens B review asking for more substance. Being verbose does not help if the content is generic.

Focus areas:

  • Color: 3050 lines (palette tables + CSS blocks)
  • Components: 100200 lines (36 components, fully specified)
  • Visual Theme: 3040 lines (atmosphere + use cases + prior art)
  • Anti-patterns: 815 lines (one per key mistake to avoid)

The mission-control design system (design-systems/mission-control/DESIGN.md) is a good reference — tight scope (3 primary colors, dark only, 6 components).