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
Miguel Palhas c856333e4a
ci / rust (push) Successful in 2m40s
docs(maestro): describe the single-host architecture
Rewrites CLAUDE.md as the current-architecture reference, drops the
registry from INSTALL/PRODUCT/DESIGN and the web README, deletes the
registry Dockerfile, and marks the dated design specs superseded where
they describe the registry, JWT auth, or cross-host attach.

The installer loses its configure step — every config field now has a
default, so a daemon starts with no config file at all.
2026-08-22 10:26:49 +01:00

116 lines
5.4 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 web 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). `≥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); session tree (collapsible
directory folders with counts, leaf rows with status dot, name, command meta,
exited badge, reveal-on-hover kill). Empty states are distinct and teach:
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
daemon: `?mock` (seed sessions), `?mock&spawn` (open the spawn modal),
`?theme=light|dark` (pin a theme). Guarded by `import.meta.env.DEV`.