From 5f0dcda2abe80796129880aab76ce447e578257e Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 12 Jul 2026 13:57:35 +0200 Subject: [PATCH] docs: Pressing Plant frontend Phase 1 implementation plan Co-Authored-By: Claude Opus 4.8 (1M context) --- ...26-07-12-frontend-pressing-plant-phase1.md | 395 ++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-12-frontend-pressing-plant-phase1.md diff --git a/docs/superpowers/plans/2026-07-12-frontend-pressing-plant-phase1.md b/docs/superpowers/plans/2026-07-12-frontend-pressing-plant-phase1.md new file mode 100644 index 0000000..bc6f604 --- /dev/null +++ b/docs/superpowers/plans/2026-07-12-frontend-pressing-plant-phase1.md @@ -0,0 +1,395 @@ +# Pressing Plant Frontend — Phase 1 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Give Lyra the "Pressing Plant" visual identity — design tokens + fonts, a shared component library, an app shell (masthead + contents-nav + theme toggle), and a fully rebuilt home / "The Floor" page — without breaking the existing web test suite. + +**Architecture:** Hand-rolled CSS (no framework) via two global stylesheets — `globals.css` (tokens for both themes, reset) and `design.css` (component classes) — imported once in the root layout. Shared React components under `web/src/app/_ui/`. The home page reads existing `/api/requests`, `/api/wanted`, `/api/artists` data as-is; a pure `describeJob()` helper maps pipeline `state`/`currentStage` to a literal label + severity kind + stepped progress. + +**Tech Stack:** Next.js 15.5 (App Router), React 19, TypeScript, vitest + Testing Library, self-hosted font via `next/font/local` (fallback `next/font/google`, ultimate fallback system serif stack), Playwright for visual verification. + +## Global Constraints + +- Spec: `docs/superpowers/specs/2026-07-12-frontend-design-system.md`. Comp: https://claude.ai/code/artifact/af6f9654-619d-4245-b3dd-602a6427934a +- **Palette (verbatim), light / dark:** `--paper` `#E7DFCC`/`#14130F`, `--paper-2` `#DED5BE`/`#1D1B16`, `--ink` `#1B1A17`/`#E9E1CD`, `--graphite` `#726D5F`/`#938C7B`, `--rule` `rgba(ink,.16)`, `--rule-2` `rgba(ink,.30)`/`rgba(ink,.28)`, `--accent` `#1F5138`/`#5BB487`, `--accent-ink` `#F3EEDF`/`#10120F`, `--live` `#9A6B1E`/`#D2A24E`, `--alert` `#99392B`/`#D8695A`. +- **Type:** `--serif` = Fraunces stack, `--mono` = `ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas, monospace`. Uppercase mono labels get letter-spacing 0.12–0.22em. `tabular-nums` on aligned digits. `text-wrap: balance` on headings. +- **Theme mechanics:** tokens on `:root`; redefine under `@media (prefers-color-scheme: dark)` AND under `:root[data-theme="dark"]` / `:root[data-theme="light"]` (explicit toggle wins both ways). Style only through tokens. +- **Sharp corners** (radius 0–2px). Faint paper grain. `prefers-reduced-motion` honored. Visible `:focus-visible`. +- **Metaphor = chrome only.** Functional status labels literal (Downloading, Verifying/Tagging, Needs attention). Never obscure function. +- **Preserve test hooks:** keep existing `aria-label`s and button text (`aria-label="artist"`, `aria-label="album"`, button "Request", etc.). Any deliberate copy change updates its test in the same commit. +- Commands run from `web/`. Tests: `npm test` (vitest, DB `lyra_test`). Typecheck: `npx tsc --noEmit`. + +--- + +### Task 1: Fonts + design tokens + base stylesheet + +**Files:** +- Create: `web/src/app/globals.css` +- Create: `web/src/app/fonts.ts` +- Modify: `web/src/app/layout.tsx` +- (Vendor) `web/src/fonts/Fraunces.woff2` (if self-hosting succeeds) + +**Interfaces:** +- Produces: global CSS custom properties (`--paper`, `--ink`, `--graphite`, `--rule`, `--rule-2`, `--accent`, `--accent-ink`, `--live`, `--alert`, `--serif`, `--mono`, `--shell`) available to all later tasks; `fraunces` font object exposing `.variable` (sets `--font-fraunces`). + +- [ ] **Step 1: Obtain the display font (self-host, with fallbacks).** + +Try, in order, and stop at the first that works: +1. Vendor Fraunces variable woff2: + ```bash + mkdir -p web/src/fonts + curl -fsSL -o web/src/fonts/Fraunces.woff2 \ + "https://github.com/undercasetype/Fraunces/raw/master/fonts/webfonts/Fraunces%5BSOFT,WONK,opsz,wght%5D.woff2" + ``` + Then `web/src/app/fonts.ts`: + ```ts + import localFont from "next/font/local"; + export const fraunces = localFont({ + src: "../fonts/Fraunces.woff2", + variable: "--font-fraunces", + display: "swap", + fallback: ["Palatino Linotype", "Palatino", "Book Antiqua", "Georgia", "serif"], + }); + ``` +2. If the download is blocked, use Google at build time instead — `web/src/app/fonts.ts`: + ```ts + import { Fraunces } from "next/font/google"; + export const fraunces = Fraunces({ subsets: ["latin"], variable: "--font-fraunces", display: "swap" }); + ``` +3. If neither works (offline build), skip `fonts.ts`, and in `globals.css` set `--serif` to the system stack `"Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif`. The `--serif` indirection makes this graceful. + +- [ ] **Step 2: Write `web/src/app/globals.css`.** + +Copy the token + base block from the comp (`scratchpad/lyra-mockup.html` `