mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
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
This commit is contained in:
parent
1bf7836471
commit
4c15ea45fa
1 changed files with 328 additions and 0 deletions
328
docs/design-systems.md
Normal file
328
docs/design-systems.md
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
# Design System Authoring Guide
|
||||
|
||||
**Parent:** [`spec.md`](spec.md) · **Siblings:** [`architecture.md`](architecture.md) · [`skills-protocol.md`](skills-protocol.md) · [`agent-adapters.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:
|
||||
|
||||
```markdown
|
||||
# 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.
|
||||
|
||||
```css
|
||||
/* 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:
|
||||
|
||||
```css
|
||||
: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:
|
||||
|
||||
```css
|
||||
.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:
|
||||
|
||||
```css
|
||||
/* 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:**
|
||||
|
||||
```css
|
||||
/* 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:
|
||||
|
||||
```css
|
||||
/* 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
|
||||
|
||||
```css
|
||||
--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 array** — `buildLocalizedContentIds()` 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:
|
||||
|
||||
```markdown
|
||||
## 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 300–600 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: 30–50 lines (palette tables + CSS blocks)
|
||||
- Components: 100–200 lines (3–6 components, fully specified)
|
||||
- Visual Theme: 30–40 lines (atmosphere + use cases + prior art)
|
||||
- Anti-patterns: 8–15 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).
|
||||
Loading…
Reference in a new issue