Files
Lyra/docs/superpowers/specs/2026-07-12-frontend-design-system.md
T
Jonathan 4655d815ee docs: Pressing Plant frontend design system spec (Phase 1)
Design language + Phase 1 plan (design system + app shell + home page).
Comp signed off. Liner Notes / record-label identity, chrome-only
metaphor, manila+spruce palette, Fraunces/mono, state-as-form.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 13:53:52 +02:00

129 lines
7.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.
# Lyra frontend: the "Pressing Plant" design system
**Date:** 2026-07-12
**Status:** approved (design comp signed off), implementing
**Comp:** https://claude.ai/code/artifact/af6f9654-619d-4245-b3dd-602a6427934a
## Intent
Give Lyra a real visual identity instead of unstyled raw HTML. Direction:
**Liner Notes / "Pressing Plant"** — the app presented as *a record label's internal
catalog for an audience of one*. The acquisition pipeline reads as a pressing plant.
The metaphor lives in the **chrome only** (masthead, section names, catalog/matrix
numbers, deadwax etch styling); **functional labels stay literal and legible**
(Downloading, Verifying, Needs attention) so nothing is obscured. Single-user homelab
tool → license to be bold.
This is a **tool, not a document**: information design first (summary before detail,
state encoded as form — severity stripe + chip — not just prose), editorial typography
second.
## Design language (the reusable reference)
### Color — deliberately off the AI-editorial cliché (no cream, no vermilion)
Two-color-print feel: manila/kraft paper + warm ink + one bold **deep spruce** accent.
Tokens are CSS custom properties on `:root`; both themes get equal care. Semantic status
colors are **separate from the accent**.
| Token | Day (light) | Night (dark) | Use |
|---|---|---|---|
| `--paper` | `#E7DFCC` | `#14130F` | ground |
| `--paper-2` | `#DED5BE` | `#1D1B16` | recessed panel |
| `--ink` | `#1B1A17` | `#E9E1CD` | primary text |
| `--graphite` | `#726D5F` | `#938C7B` | secondary text / mono meta |
| `--rule` | `rgba(ink,.16)` | `rgba(ink,.16)` | hairlines |
| `--rule-2` | `rgba(ink,.30)` | `rgba(ink,.28)` | stronger rules |
| `--accent` | `#1F5138` | `#5BB487` | the one bold move; links, active nav, "pressed" |
| `--accent-ink` | `#F3EEDF` | `#10120F` | text on accent |
| `--live` (semantic) | `#9A6B1E` amber | `#D2A24E` | work in progress |
| `--alert` (semantic) | `#99392B` clay | `#D8695A` | needs attention (errors only) |
Theme mechanics: define tokens on `:root`; redefine under
`@media (prefers-color-scheme: dark)`; **also** redefine under
`:root[data-theme="light"]` and `:root[data-theme="dark"]` so an explicit in-app toggle
wins in both directions. Style components through tokens only, never inside the media
query. Faint paper grain via a tiled radial-gradient. **Sharp corners** (radius 02px) —
letterpress/label feel, and it dodges the `rounded-lg` cliché.
### Type — serif/mono tension is the concept
- **Display + editorial:** **Fraunces** (OFL variable serif; soft, optical, warm),
self-hosted via `next/font/local` (woff2 vendored under `web/src/fonts/`, subset to
latin). The comp used a Palatino system stack as a stand-in; the real app ships Fraunces.
- **Machine voice (all metadata, labels, data, progress):** a system **mono** stack
(`ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace`) — zero weight,
reliable. Uppercase mono labels get `letter-spacing: 0.120.22em`.
- Scale (rem): masthead `clamp(2.6, 6vw, 3.7)`, job title `1.42`, tile value `2.5`,
body `1.0`, section head `0.78` mono, meta/labels `0.620.72` mono. `text-wrap: balance`
on headings; running text ~65ch; `font-variant-numeric: tabular-nums` on all aligned digits.
### Motifs
Masthead wordmark + tagline + live worker dot + "Pressing № NNNN" catalog mark;
contents-bar nav (hairline top/bottom, active item underlined in accent); summary tiles
(hairline grid); dept section headers (mono small-caps + rule fill + count); tracklist
job rows with a left **severity stripe**; mono **status chip**; thin ruled **progress bar**
(indeterminate variant for search); **deadwax matrix** etch line for pressed items
(`Lyra·A0430 — FLAC 24/48`); colophon footer.
### Motion (restrained)
Live-dot pulse; indeterminate progress slide; hover underlines. All gated by
`@media (prefers-reduced-motion: reduce)`. No scattered effects.
## Architecture
Hand-rolled CSS, no framework (matches the lightweight Next 15 / React 19 stack).
- **`web/src/app/globals.css`** (new) — token definitions (both themes), base/reset,
paper grain, focus-visible, reduced-motion. Imported once in the root layout.
- **`web/src/app/design.css`** (new, or co-located module) — the component classes
(masthead, contents nav, tiles, dept header, job row, chip, bar, pressed list, buttons,
inputs). Plain global classes keep it simple and shared across pages.
- **Fonts:** `web/src/fonts/` vendored Fraunces woff2 + `next/font/local` wiring, exposed
as a CSS variable on `<body>`.
- **App shell:** `web/src/app/layout.tsx` gains the shared chrome — a `<Masthead/>` +
`<ContentsNav/>` server components wrapping `{children}` in the editorial column, plus a
small client **theme toggle** that stamps `data-theme` on `<html>` and persists to
`localStorage`. Individual page files stop repeating the ad-hoc nav line.
- **Components** live under `web/src/app/_ui/` (e.g. `masthead.tsx`, `contents-nav.tsx`,
`theme-toggle.tsx`, `stat-tiles.tsx`, `job-row.tsx`, `status-chip.tsx`, `progress-bar.tsx`,
`section-header.tsx`). Each: one clear job, styled via the shared classes, driven by props.
## Rollout (this spec covers Phase 1; later phases are their own plans)
**Phase 1 — system + shell + flagship (this plan):**
1. Fonts + `globals.css` tokens (both themes) + base reset, wired into the root layout.
2. Component library (`_ui/`) + `design.css` classes: masthead, contents-nav, theme-toggle,
stat-tiles, section-header, job-row, status-chip, progress-bar, pressed-list.
3. App shell in `layout.tsx` (masthead + nav + column + toggle).
4. Rebuild the **home / "The Floor"** page (`app/page.tsx` + `queue.tsx`) on the real
queue data: summary tiles, on-the-press job list (state → stripe+chip+bar, real pipeline
stages mapped to literal labels: resolving/searching/downloading/verifying/tagging;
needs-attention shows recovery actions), recently-pressed list, empty state.
**Phase 2 (later):** restyle Artists, Artist detail, Discover, Discover/artist preview,
Wanted onto the same system.
**Phase 3 (later): Settings redesign** — the paths + auth pain point. Editorial form
treatment for credentials (Qobuz, slskd) with clear "set/unset" affordances; a **Library**
section surfacing the configured music path. NOTE/constraint: `MUSIC_DIR` is a container
mount, not runtime-editable from the UI (changing a string can't remount a Docker volume),
so paths are **shown with explanation** (and any genuinely runtime-config paths made
editable); credentials and `discover.*`/`monitor.*`/`scan.chunkSize` become first-class,
well-labelled controls. Scoped in its own spec when we reach it.
## Testing & verification
- The existing web suite (vitest, 71 tests) tests **routes/logic**, not visuals; a restyle
must keep it green. **Preserve accessible names / test hooks** — existing `aria-label`s,
button text ("Save", "Search"), and `aria-label="scan result"` etc. Update a test only
where copy/behavior intentionally changes, and in the same commit.
- New interactive component (theme toggle) gets a small test (persists + toggles
`data-theme`).
- **Visual verification:** drive the real app (playwright) and eyeball the home page in
**both themes** + a mobile width before calling Phase 1 done — screenshots, not just
"it compiles".
- Accessibility: visible `:focus-visible`, `prefers-reduced-motion` honored, contrast
legible on both grounds, keyboard-navigable nav + toggle.
## Out of scope (Phase 1)
Phases 23 pages; any backend/data changes (home reads existing queue/config data as-is);
new fonts beyond Fraunces + system mono; charts.