From 4463add59c8ef19d5e85d257db82af7c968b2698 Mon Sep 17 00:00:00 2001 From: Charles Sexton Date: Sat, 27 Jun 2026 13:15:51 +0100 Subject: [PATCH 1/2] strengthen the design contract, instantiation, and tasks for design fidelity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The built app diverged from the target design: broken hero image, modal rating stars as grey outlines instead of filled orange, boxed stats, monospace input. The contract specified these correctly; the instantiation and tasks did not enforce it, and there was no visual gate, so tsc/lint/test passed while the page looked wrong. - design-contract §4.9: build the Rating once (one component, three modes); picker marks are filled signal/primary, not outline-only - design-contract §4.10: inputs/textareas inherit the Barlow UI font, not the user-agent monospace default; §8 checklist gains rating-fill, brand-media- renders, and non-monospace items - instantiation §3.0: brand-asset wiring (copy into src/assets or public, never import across the specs/ boundary); §3.2 hero required + must render; §3.3 stat cluster not boxed; new §3.8 shared Rating component - AGENTS.md: design-bearing tasks get a visual check (verify is necessary, not sufficient); tasks 2/3/4 gain concrete "run the app" gates Co-Authored-By: Claude Opus 4.8 (1M context) --- AGENTS.md | 6 ++++ specs/design-contract.md | 39 ++++++++++++++++++----- specs/session-feedback-instantiation.md | 41 +++++++++++++++++++++++-- specs/tasks/task-2-home-list.md | 11 ++++++- specs/tasks/task-3-rate-modal.md | 9 +++++- specs/tasks/task-4-branding.md | 17 +++++++--- 6 files changed, 105 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index de0b992..32e0f6c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -84,6 +84,12 @@ diverge from its spec. - **Per task:** typecheck + lint (near-instant). **On the data-layer task:** the audit pass (the checklist in tasks.md Task 1). The test suite exists from Task 1's smoke test and peaks at the modal milestone (Task 3). +- **Design-bearing tasks get a visual check (`verify` is necessary, not sufficient).** `tsc`, lint, + and test cannot see a broken hero image, a rating picker whose stars never fill, a monospace input, + or stats rendered as boxes instead of the contract's cluster. On Tasks 2, 3, and 4, **run the app + and look at it** against the conformance checklist (contract §8) and the instantiation before + calling the task done. This is the design analogue of the data-layer audit pass: green `verify` plus + eyes on the rendered surface, not green `verify` alone. - **Commit each task when its gate is green.** After `npm run verify` passes (and the audit pass where the task requires it), commit that task with a short message naming it, before starting the next. The git log is the build record; never bundle two tasks into one commit. diff --git a/specs/design-contract.md b/specs/design-contract.md index c26e58c..23527f3 100644 --- a/specs/design-contract.md +++ b/specs/design-contract.md @@ -352,6 +352,14 @@ one interactive mode and two static read-out modes. It is reusable wherever a qu judgement, score, or progress-out-of-N is shown; an app that has no such concept simply does not use it. The glyph and range are parameters; everything below is fixed. +> **Build it once.** This is **one component** with a `mode` (picker / inline / confirmation), not +> three look-alike blocks of star markup. Every surface that shows marks (the card read-out, the modal +> picker, the success confirmation) renders the *same* component; only `mode`, `value`, and the +> handlers differ. Per-surface bespoke star code is the failure this section exists to prevent: it is +> how the fills drift, how the picker ends up with unfilled outline marks, and how the same rating +> renders three different ways. If you find yourself writing a second set of star classes, stop and +> reuse the first. + Two fills carry meaning and must not be swapped: - **Active fill** = `signal/primary` — used while the user is *choosing* and on the immediate *confirmation*. It is the live signal. @@ -360,13 +368,18 @@ Two fills carry meaning and must not be swapped: **Picker (interactive).** A centred row of N mark-buttons, each `clamp(46px,11vw,60px)`, no fill or border on the button itself, `motion/control` (slight overshoot, selected mark `scale(1.06)`). A -mark is "on" when its index is `<= max(hover, draft)`. On marks: glyph filled `signal/primary`, -stroke `signal/primary`. Off marks: glyph filled `rgba(255,255,255,.04)`, stroke -`rgba(255,255,255,.32)` at `stroke-width:1.4`. Hover/focus previews without committing; click -commits the draft; pointer-leave clears the preview. A live caption sits beneath at a reserved -height (~26px, so nothing shifts): it names the current value, tinted `signal/primary` at the top of -the range and `text/label` below, and is blank at zero. Every mark-button carries an `aria-label` -("3 stars"). +mark is "on" when its index is `<= max(hover, draft)`. On marks: glyph **filled** `signal/primary` +(`fill: #F5400D`, not just a coloured stroke), stroke `signal/primary`. Off marks: glyph filled +`rgba(255,255,255,.04)`, stroke `rgba(255,255,255,.32)` at `stroke-width:1.4`. Hover/focus previews +without committing; click commits the draft; pointer-leave clears the preview. A live caption sits +beneath at a reserved height (~26px, so nothing shifts): it names the current value, tinted +`signal/primary` at the top of the range and `text/label` below, and is blank at zero. Every +mark-button carries an `aria-label` ("3 stars"). + +> The single most common build error here is a picker whose marks are **thin grey outlines that never +> fill** on hover or select. That is wrong: a selected mark is a solid orange star. If your stars look +> like the off state when a rating is chosen, the `fill` is missing or is being applied to a stroke +> only. A picker that does not visibly fill fails conformance. **Inline read-out (settled).** A compact, non-interactive row of small marks (~17px) showing a persisted rating: filled marks `accent/gold`, the remainder `accent/gold-empty`. Pairs with a short @@ -385,6 +398,10 @@ filled marks `signal/primary` up to the chosen value, the remainder `accent/gold `outline:none`, placeholder in `text/faint`. Label above in 14px 700 `text/muted-2`, with an "(optional)" / "(required)" qualifier in `text/faint` 500 where relevant. +> Set `font-family` to `font/ui` (Barlow) explicitly on inputs and textareas. Browsers default a +> `