This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/DESIGN.md
T
naps62-yoloandClaude Opus 4.8 e909790b56 docs(maestro): add PRODUCT.md + DESIGN.md design context
Capture the web UI's strategic register (product) and the visual design
system (OKLCH token palette, monospace-first type, dark+light theming) for
the impeccable workflow. Supersedes the transient .impeccable.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 18:40:42 +00:00

117 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design
Visual system for the Maestro web UI. Source of truth is
`web/src/styles.css` (`:root` token block); this document mirrors it. Register:
**product** — design serves the task. See [PRODUCT.md](PRODUCT.md) for strategy.
## Theme
Dark is the default (terminal context); light is a full peer. Everything is
driven by CSS custom-property tokens, so a theme is a token swap, never a
restyle. Resolution order:
1. `:root[data-theme="light"|"dark"]` — explicit user pin (persisted in
`localStorage` as `maestro-theme`), set by the header toggle.
2. `@media (prefers-color-scheme: light)` — followed when the user hasn't
pinned a theme.
3. Otherwise the `:root` defaults (dark).
The xterm terminal stays dark in both themes by convention; `--term-bg` is dark
in light mode too.
## Color (OKLCH)
Strategy: **Restrained** (product floor) with one committed signal hue —
**green = "alive"**, carrying running status, primary action, focus, and
selection. Neutrals are a cool slate (hue ~256). Green is the signal, never a
costume: text stays neutral high-contrast, surfaces stay slate, so the result
reads terminal-native without CRT novelty.
| Role | Dark | Light |
|---|---|---|
| `--bg` | `oklch(0.178 0.012 256)` | `oklch(0.985 0.003 256)` |
| `--surface` | `oklch(0.214 0.014 256)` | `oklch(1 0 0)` |
| `--surface-2` | `oklch(0.252 0.016 256)` | `oklch(0.965 0.004 256)` |
| `--surface-3` | `oklch(0.292 0.018 256)` | `oklch(0.93 0.006 256)` |
| `--border` | `oklch(0.318 0.018 256)` | `oklch(0.892 0.008 256)` |
| `--border-strong` | `oklch(0.4 0.022 256)` | `oklch(0.82 0.012 256)` |
| `--ink` (body text) | `oklch(0.945 0.006 256)` | `oklch(0.26 0.014 256)` |
| `--muted` (secondary) | `oklch(0.722 0.014 256)` | `oklch(0.47 0.016 256)` |
| `--faint` (tertiary/decorative) | `oklch(0.58 0.014 256)` | `oklch(0.62 0.016 256)` |
| `--accent` / `--live` | `oklch(0.83–0.84 0.16–0.17 152)` | `oklch(0.6 0.15–0.16 151)` |
| `--accent-ink` (text on accent) | `oklch(0.2 0.03 256)` | `oklch(0.99 0.01 152)` |
| `--danger` | `oklch(0.72 0.18 25)` | `oklch(0.55 0.21 27)` |
| `--warn` | `oklch(0.82 0.14 85)` | `oklch(0.66 0.15 75)` |
Body and secondary text meet WCAG AA (≥4.5:1) against their surface in both
themes; `--faint` is reserved for decorative/large use only. Status never relies
on color alone — a running dot pulses and is filled; a dead session is a hollow
ring **and** carries an "exited" badge.
Tints/glow derive from the accent at low alpha: `--accent-tint` (0.12),
`--accent-ring` (focus, ~0.55), `--live-glow` (status halo).
## Typography
**Monospace is the UI typeface** (brand identity), system-stack only (offline-
safe, zero web-font dependency, embedded clean into the registry binary):
- `--font-mono`: `ui-monospace, "SF Mono", "Cascadia Code", "JetBrains Mono", "Roboto Mono", Menlo, Consolas, monospace` — chrome, labels, data, ids, the wordmark.
- `--font-sans`: `system-ui` stack — reserved for the few prose strings (empty-state sentences).
Fixed rem scale (product register; not fluid/clamped): `--fs-11` 0.6875rem
through `--fs-20` 1.25rem, in ~1.12–1.2 steps. Base body `--fs-15` (0.9375rem),
line-height 1.45. No display sizes — the largest type is the 1.25rem wordmark.
## Spacing, radius, sizing
- Spacing scale: `--sp-1`..`--sp-8` = 4 / 8 / 12 / 16 / 20 / 24 / 32 px.
- Radius: `--r-1` 6 · `--r-2` 9 · `--r-3` 14 · `--r-pill` 999.
- Touch/row sizing: `--row-h` 46, `--control-h` 38, `--tap` 40 (mobile-first
touch targets).
## Motion
State and feedback only; 120–240 ms, ease-out (`cubic-bezier(0.22,1,0.36,1)`),
no bounce. Tokens `--dur-fast`/`--dur`/`--dur-slow`.
- Living signals: brand dot + running-status ripple pulse (the "alive" cue);
`connecting` state pill pulses.
- Interaction: row hover/focus, button press (1px), input focus ring, chevron
rotation, theme-toggle sun/moon swap.
- Modal: bottom-sheet slide-up on mobile, scale-pop on ≥600px.
- Every animation has a `prefers-reduced-motion: reduce` path (pulses off,
transitions near-instant).
## Layout & structure
Mobile-first, structural responsiveness (not fluid type). Single `≤520px`
breakpoint stacks the header (full-width search, wrapped actions) and drops the
daemon chips to their own wrapped line. `≥600px` switches the spawn modal from
bottom-sheet to centered dialog. A faint slate grid texture sits on `--bg` as
terminal atmosphere (very low contrast, not decoration).
## Components
Three surfaces, all DOM built in TS, styled by class:
- **Catalog** — sticky blurred header (pulsing wordmark, search, theme toggle,
Spawn); status bar (`N sessions · M live` tally + daemon chips); session tree
(collapsible folders with counts, leaf rows with status dot, name, `@daemon`
+ command meta, exited badge, reveal-on-hover kill). Empty states are
distinct and teach: waiting-for-daemon, no-sessions, no-search-match.
- **Attach** — header (back, `session <id>`, live/closed/error state pill) over
a framed dark xterm with a green cursor.
- **Spawn modal** — bottom-sheet/dialog form; mono inputs with green focus
ring; grouped worktree fieldset.
Interactive elements ship default / hover / focus-visible / active / disabled
states; the accent green is used only for action, selection, focus, and live
status — never decoration.
## Dev review aids (DEV-only, stripped from production)
`web/src/mock.ts` + query params seed a representative catalog without a running
registry: `?mock` (seed sessions/daemons), `?mock&spawn` (open the spawn
modal), `?theme=light|dark` (pin a theme). Guarded by `import.meta.env.DEV`.