Skip to content

Commit 1847b77

Browse files
angusbezzinaclaude
andcommitted
feat(web): two themes, one icon set, and a backdrop you can see move
Bundles the dark-theme work that was sitting uncommitted with a pass of maintainer feedback taken off the built page. From the earlier session, unlanded until now: a three-state theme system (light / dark / system) resolved before first paint, code plates that scroll through a Radix ScrollArea in the page's own ink, the real IBM Plex Mono 600, and lucide-react. This pass: · The theme control moved twice and ended up as the third link in the masthead row, with a label. A row of its own read as a fourth line of the masthead; a bare glyph could not carry an underline that lined up with the links', because a 20 px icon centred in its box sits closer to its bottom edge than text does to its. A label gives it a line box and the rule places itself. Both mode words render into one grid cell with the inactive one hidden, so the row cannot change width when the mode flips. · Every icon is Lucide, through src/components/Icon.tsx, at one stroke weight and one size: 1 em of the type it sits in. The fixed 20 px "control" size did not survive contact with the page — both controls using it sat beside 11 px machine text. · The backdrop actually moves. It was drifting at roughly half a pixel per second at 6 % ink, which is an animation nobody receives. Now 3-7 px/s on 34-82 s round trips, alternating rather than snapping home, with the fade on its own clock so a frame is not visible only while it is stopping. · Machine text in running prose is marked with backticks in copy.ts and rendered as <code> by src/markup.tsx. copy.ts stays a file of strings: the typography test walks every exported value, and JSX would have sent it into a React element's props. · Capture frames take --color-rule-2 instead of the accent. The accent is what separates the page from itself; spending it on a picture put an orange box around the one thing meant to be looked at. · The footer band is an identity block left, links right on the wordmark's baseline. The sub-footer is an accent-filled band. · The GPU CLI link hovers to #19DC6A, stored as a hex because an oklch fitted to it painted #18DC6A in Chrome. · §01's narrow column gets a floor above 85 rem. Its snippet overflowed at 1600 px and fit at 1280, because the shell's content box gets narrower as the viewport widens past --page-max while --page-gutter keeps growing. One real bug fixed on the way: .copy:hover:not(:disabled) is (0,3,0) against .copy[data-state="copied"] at (0,2,0), and the pointer is always on the button when it reports, so every successful copy drew its tick in ink rather than the accent. Gate 26 checked that all eight states existed; nothing checked they could be reached. Gates: 119 tests, typecheck, responsive 5/5. Contrast is 28/30 with 2 waived — #19DC6A on the accent band measures 3.34:1 light and 1.41:1 dark, which is the collision of two explicit instructions and is written up at gate 40 and filed as annotkit-lto rather than quietly corrected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 3958951 commit 1847b77

33 files changed

Lines changed: 2318 additions & 302 deletions

.beads/interactions.jsonl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,23 @@
33
{"id":"int-6f3d5195f734cd5d3614c41b1f8dac01","kind":"field_change","created_at":"2026-08-19T21:49:48.345784Z","actor":"Angus Bezzina","issue_id":"annotkit-op6.3","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Implemented: world context on every note (provider + ANNOTKIT_CONTEXT*, per-capture snapshot, markdown/JSON/MCP), env-driven install and sink destinations (AnnotationEnvironment + MultiSink), append-only JSONL event stream (JSONLEventSink, O_APPEND, diff-per-flush), and end-to-end validation via AnnotKitEnvProbe + AgentLoopE2ETests. 264 tests pass."}}
44
{"id":"int-f3a9554938910afe979932d09de7bc60","kind":"field_change","created_at":"2026-08-19T21:49:48.723087Z","actor":"Angus Bezzina","issue_id":"annotkit-op6.4","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Implemented: world context on every note (provider + ANNOTKIT_CONTEXT*, per-capture snapshot, markdown/JSON/MCP), env-driven install and sink destinations (AnnotationEnvironment + MultiSink), append-only JSONL event stream (JSONLEventSink, O_APPEND, diff-per-flush), and end-to-end validation via AnnotKitEnvProbe + AgentLoopE2ETests. 264 tests pass."}}
55
{"id":"int-7faec6a4cf272864b5eadba0de92b0c3","kind":"field_change","created_at":"2026-08-19T21:49:49.08739Z","actor":"Angus Bezzina","issue_id":"annotkit-op6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Implemented: world context on every note (provider + ANNOTKIT_CONTEXT*, per-capture snapshot, markdown/JSON/MCP), env-driven install and sink destinations (AnnotationEnvironment + MultiSink), append-only JSONL event stream (JSONLEventSink, O_APPEND, diff-per-flush), and end-to-end validation via AnnotKitEnvProbe + AgentLoopE2ETests. 264 tests pass."}}
6+
{"id":"int-61aa07351ae8335b7728c3068bd60133","kind":"field_change","created_at":"2026-08-20T13:36:06.06407Z","actor":"Angus Bezzina","issue_id":"annotkit-45d.1","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Diagnosis corrected against a render: §04's two columns end 13px apart (the page's most balanced pair) — the void was its 4rem close stacking under §05's 6rem open, 160px, the widest transition on the page under the thinnest section. §04 now closes at 1.5rem above 60rem (120px transition). Single-column and align-self:end were rendered and rejected. §01 (197px) and §03 (269px) column voids accepted as intentional. Recorded in web/.hallmark/slop-test.md."}}
7+
{"id":"int-c6105de1465cbd45698892940d72075f","kind":"field_change","created_at":"2026-08-20T13:36:06.632502Z","actor":"Angus Bezzina","issue_id":"annotkit-45d.2","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Tagline split into two sentence blocks in one <p>; second sentence lost its repeat of 1.0 — now 'Pre-1.0 — the API may still shift.' Measure-tightening rejected: the sentence-boundary break needs ~38ch, which gate 25 bans. Verified one line per sentence at 1280/768/414/375; at 320 the first sentence wraps inside itself but the sentence boundary is still a line boundary."}}
8+
{"id":"int-a913f50545853b77cfe52947d0437c43","kind":"field_change","created_at":"2026-08-20T13:36:15.875654Z","actor":"Angus Bezzina","issue_id":"annotkit-45d","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Both children closed; web/.hallmark/slop-test.md carries an 'Audit follow-ups · 2026-08-20' section with the decisions and the rejected options. Gates re-run green: responsive 5/5, contrast 14/14, vitest 95/95, build clean."}}
9+
{"id":"int-b5456792d66e94883a30e6244f3f6191","kind":"field_change","created_at":"2026-08-20T13:45:35.407061Z","actor":"Angus Bezzina","issue_id":"annotkit-562","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Band now anchors wordmark left / tagline right via auto tracks + space-between above 48rem; colophon hairline promoted from a border on the 66ch paragraph (stopped at 44% of the shell) to a full-width <hr> seam. Footer holds the full measure at 1280/1440; mobile stack unchanged."}}
10+
{"id":"int-0baafc07ca701592962734cb60fa7305","kind":"field_change","created_at":"2026-08-20T13:45:35.934066Z","actor":"Angus Bezzina","issue_id":"annotkit-bpr","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"mast__inner gap --space-sm (12px) -> --space-lg (24px). --space-xl rendered and rejected: floats the nav free of the wordmark. Gate 44b re-verified at 1280x800 — hero CTAs bottom out at 661px, 139px of headroom."}}
11+
{"id":"int-74851ca5bbd74251f739720bc1aa7070","kind":"field_change","created_at":"2026-08-20T13:53:49.921976Z","actor":"Angus Bezzina","issue_id":"annotkit-8st","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Radix ScrollArea (@radix-ui/react-scroll-area 1.2.18) wraps the code plates; styled from existing tokens, no Tailwind/CVA. Viewport keeps tabIndex=0 (the pre no longer scrolls itself); .code__plate needs min-width:0 or a long line drags the page 217px sideways at 375px (gate 34); thumb is --color-muted not --color-rule-2, which measures 2.78:1 on paper-2 — pair added to contrast.mjs. ResizeObserver stubbed in tests/setup.dom.ts for jsdom. Cost +7.0KB gzip (67.6 -> 74.6), recorded in slop-test.md."}}
12+
{"id":"int-06b56f02a720a1adc0f68d54ab612b71","kind":"field_change","created_at":"2026-08-20T16:14:06.828817Z","actor":"Angus Bezzina","issue_id":"annotkit-zhi","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"Removed border-block-end from .code__bar (5 instances). Scanned the built page for any horizontal line within 40px above an element with a corner radius >= 6px: .code__bar over .code__plate was the only match, and the re-scan after the change returns empty. Figures/video carry no rule above them; the term lists' top hairline sits above prose, not a rounded plate, so it stays. Gates green."}}
13+
{"id":"int-c1ecc242163d1ae87bda3e730edda987","kind":"field_change","created_at":"2026-08-20T16:49:18.809078Z","actor":"Angus Bezzina","issue_id":"annotkit-cuf","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
14+
{"id":"int-965b24af11455dcf562be429e041f690","kind":"field_change","created_at":"2026-08-20T16:49:19.012137Z","actor":"Angus Bezzina","issue_id":"annotkit-ci8","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
15+
{"id":"int-a590ea77ee752a0f6696a47a2a857022","kind":"field_change","created_at":"2026-08-20T16:49:19.212254Z","actor":"Angus Bezzina","issue_id":"annotkit-cnz","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
16+
{"id":"int-df4722d2dc43b72384386a3cb6bc86ea","kind":"field_change","created_at":"2026-08-20T16:49:19.453171Z","actor":"Angus Bezzina","issue_id":"annotkit-9de","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
17+
{"id":"int-2f725d400ebcff1716f96faa46f16090","kind":"field_change","created_at":"2026-08-20T16:49:19.658125Z","actor":"Angus Bezzina","issue_id":"annotkit-s4k","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
18+
{"id":"int-369c6ef2b865267ef1bd205ff0c96bb4","kind":"field_change","created_at":"2026-08-20T16:49:20.282145Z","actor":"Angus Bezzina","issue_id":"annotkit-lhe","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Dark palette re-derived (same warm hue band, chroma floor >= 0.011), pre-paint inline script, three-state choice (system/light/dark) with localStorage, toggle in the masthead nav. contrast.mjs now READS tokens.css instead of hand-mirroring and checks both palettes: 28/28. tests/theme.dom.test.ts ties the inline script's literals to src/theme.ts."}}
19+
{"id":"int-e6940272cbc6913897610ad755c55112","kind":"field_change","created_at":"2026-08-20T16:49:20.768647Z","actor":"Angus Bezzina","issue_id":"annotkit-giw","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Whole page in a Radix ScrollArea. html/body moved to overflow:clip on BOTH axes (overflow-x:clip beside implied overflow-y:visible computes to auto, leaving the document scrollable behind the shell); #root carries the height. gate 34's probe measured only the document, which cannot overflow inside an app shell — verified blind (injected 900px block and 400-char word at 375px gave docOverflow 0, viewOverflow 525/2870), so it now measures the viewport too and throws if .page__viewport is missing. Cost: mobile URL bar no longer collapses, documented in App.tsx."}}
20+
{"id":"int-bde5e8e874c83b2e8ce38debca0373fb","kind":"field_change","created_at":"2026-08-20T17:09:15.729335Z","actor":"Angus Bezzina","issue_id":"annotkit-4m4","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified on the built page in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
21+
{"id":"int-791026621957225264db5692cdda9061","kind":"field_change","created_at":"2026-08-20T17:09:15.939379Z","actor":"Angus Bezzina","issue_id":"annotkit-6f5","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified on the built page in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
22+
{"id":"int-c011fd71162bf74bd32a70b1e95493ec","kind":"field_change","created_at":"2026-08-20T17:09:16.142185Z","actor":"Angus Bezzina","issue_id":"annotkit-ik6","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Applied and verified on the built page in both themes; recorded under Maintainer feedback in web/.hallmark/slop-test.md."}}
23+
{"id":"int-5fc8bfc7c0dfa68822c658816e6b8e94","kind":"field_change","created_at":"2026-08-20T17:09:16.762437Z","actor":"Angus Bezzina","issue_id":"annotkit-dmu","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Bare Lucide Sun/Moon at strokeWidth 1.5, in its own flow row above the masthead (not pinned: the centred dateline reaches the right edge below 48rem and would collide). 44x44 target, aria-label names the destination theme, svg aria-hidden+focusable=false. Gate 30's justification is now weaker and the record says so rather than rewriting the old argument. Fold budget re-verified: CTAs at 709px of 800."}}
24+
{"id":"int-a1b30624ca7c46e6192b68275c02f6e2","kind":"field_change","created_at":"2026-08-20T17:09:17.244552Z","actor":"Angus Bezzina","issue_id":"annotkit-bqh","extra":{"field":"status","new_value":"closed","old_value":"open","reason":"Seven hairline frames drifting and cross-fading behind the page: the app's marquee at rest, which is what makes it survive gates 29/45 where a blob field would not. CSS only, aria-hidden, pointer-events none, overflow hidden, transform+opacity only, near-prime 43-97s cycles so no two frames rephase. Reduced motion holds them still at rest opacity rather than removing them. Measured: 7,136px2 of stroke, 0.62% of a viewport before alpha, drawn at 6-9%. Gates 29 and 45 changed from plain 'no' to justifications."}}
25+
{"id":"int-0fe7247da53b0f235927a1f1fc435201","kind":"field_change","created_at":"2026-08-20T17:39:53.415873Z","actor":"Angus Bezzina","issue_id":"annotkit-6y6","extra":{"field":"status","new_value":"closed","old_value":"in_progress","reason":"All six changes landed. Theme toggle moved into the masthead link row (maintainer rejected the under-wordmark row on render). Every icon on Lucide via src/components/Icon.tsx, one stroke weight, two sizes; copy button is Copy/Check/X. Backdrop retuned from ~0.5px/s to 3-7px/s with alpha up to 10-12%. Figure frames on --color-rule-2. Subfoot link hovers to GPU CLI green (verbatim rgb(24,220,106) in dark, same hue re-derived for AA in light). Machine text in prose wrapped in <code> via a backtick convention + src/markup.tsx. Gates: 116 tests, typecheck, 30/30 contrast, responsive 5/5."}}

0 commit comments

Comments
 (0)