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>
This commit is contained in:
@@ -0,0 +1,128 @@
|
|||||||
|
# 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 0–2px) —
|
||||||
|
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.12–0.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.62–0.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 2–3 pages; any backend/data changes (home reads existing queue/config data as-is);
|
||||||
|
new fonts beyond Fraunces + system mono; charts.
|
||||||
Reference in New Issue
Block a user