You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SPA revamp: implement the chosen direction — field-first phone (B), ledger desktop (A), one attention line (C) #674
Amendment (2026-09-13). Sequence step 1's open question — "do we adopt external UI
components?" — is answered: MUI. Tailwind + shadcn/ui is declined, not deferred. The farm
palettes are kept and proven. See #674 (comment) for what
changed, which claims in the evidence comment were corrected, and the measured bundle cost; the
reasoning lives in docs/decisions/674-ui-component-library.md.
The body below is left as written, for history. The checklist at the end is current.
Tracking issue for the SPA revamp. Owner decision recorded on #656 (2026-09-02): a combination — B's field-first phone, A's ledger desktop, C's attention strip reduced to one line on both. This issue carries the work; #656 stays the home of the three visual-identity questions, which the design doc answers.
Every slice follows docs/designs/822-mui-revamp.md.
It is the component plan, not background reading — it maps each existing custom element to the
MUI component that replaces it, and states the rule: "reused, never rebuilt", and for the
shared picker, "the engine stays whole". Composing MUI primitives into a screen-specific
wrapper is expected (FilterBar, AuthShell and FieldConsole all do). Reimplementing
behaviour a shared component already has is not. #918 built a 216-line flock picker duplicating NamedEntityPicker's paging, debounce and keyboard handling — with the same constants, 50
and 250 — and its review found three defects in mechanics the shared picker had already
solved. That plan even documents the slots.paper technique for rendering content around an
Autocomplete listbox, which is the problem the bespoke component was built to work around. Cite this document in every slice brief. It appears as a ticked item below, which reads as
finished work; it is a standing reference for every slice that follows.
What was decided, and why
Three rendered directions were built from the app's own seeded data (four houses, 327/298/341 collected with House C not recorded, 63,122 eggs on hand by grade, the 14-day trend, four real orders), differing in point of view rather than palette:
A — the ledger. The screen is the working record. Ruled rows, numerals first, no cards.
B — field-first. The hen-house phone is primary; one task per screen, glove-sized targets; desktop is the same parts wider.
C — operations console. Status before records; a persistent attention strip; built for the night check.
Chosen: phone = B, desktop = A, both = one attention line. The app has exactly two real contexts — a phone in a shed and a desk in an office — and the renders designed for each were B-phone and A-desktop. (The A-phone and C-phone renders reused desktop tables at 390px and overflowed; they were not fair renders of those directions.)
Sequence
Design doc in docs/designs/ — must answer SPA: visual identity — brand-derived link colour, display optical size, two-colour mark (owner decisions) #656's three questions (link colour derived from the brand, display optical size via the already-installed Inter opsz axis, the two-colour mark) and the two questions nobody has asked: the IA (18-link flat sidebar in 5 groups, bottom tabs + "More" sheet on phone) and the layout system (today: everything is a white card; A has none, B has cards for houses only).
Also answer: do we adopt external UI components? There is no rule against them and specs/technical/tech_spec.md §8.1 (KD-6) prescribes them; the hand-built approach is a convention nobody decided. Evidence, candidates and the constraints a candidate must satisfy: #674 (comment). Ends as a decision record either way.
Grill the design doc (contrarian pass) before signoff.
Slices, each filed on this epic, sized so each can be rendered and looked at; one PR per slice; every PR attaches a 1:1 before/after comparison captured from a stack rebuilt at the head under review (the only check in this repo that reads the rendered result; it found the sole real defect in the punch-list).
The three visual-identity questions (folded in from #656, closed 2026-09-13)
#656 was closed during the issue cleanup and its questions moved here, so one decision has one
home. Its comment thread stays the record of how the direction was chosen; the substance is below. The design doc in sequence step 1 answers all three.
1 — Links derive from the brand. The palette is Slack's, token for token: #4a154b primary, #1264a3 link blue, #611f69 press (web/DESIGN.md, adopted in #52). Blue links are a second hue
that fights the "chromatic monotheism" the stylesheet header claims, and they ignore the farm accent
palettes (#149) — a forest or terracotta farm still gets Slack-blue links. Proposal: --link: var(--brand) in light, --stat-accent in night, hover a step lighter/darker. Verify
4.5:1 on --surface for every palette × theme in the styles test, not by eye.
2 — Optical size for display text. Inter everywhere at one optical size gives headings and stat
figures no voice. font-variation-settings: "opsz" 32 on h1, h2, .stat-value buys tighter
display cuts at zero download cost.
Amended 2026-09-16 (#822 D7.2, #864): the premise below was wrong in the way that matters. The files are in the package, but the app loads @fontsource-variable/inter's default entry, whose index.css carries wght faces only; the opsz faces are a separate entry (opsz.css), and adopting them costs about +119 KiB of precache (+24 KiB on the latin subset a phone fetches). "Zero download cost" above does not hold; #835 owns the swap and is charged against #825's ceiling. Left as written below for history.
Already answered, incidentally (2026-09-02): the installed @fontsource-variable/interdoes
carry the opsz axis — web/node_modules/@fontsource-variable/inter/files/ contains inter-*-opsz-normal.woff2. The issue flagged a risk that the default entry ships wght only.
It does not apply. Do not re-investigate this.
3 — The brand mark. A generic single-stroke egg outline that will not survive favicon and PWA
icon sizes. Wants a two-colour mark that holds up small.
Also inherited from #656: it was deliberately sequenced last among the seven look-and-feel
slices, because "the other six are defects; this one is a point of view." The other six shipped
(#653, #655, #660, #662, #663, #664). That ordering rationale still applies to the design doc —
answer the point-of-view questions deliberately, not as a side effect of a defect fix.
Current owner-approved screen directions
These descriptions supersede earlier visual targets for the named slices only. Implementation remains open.
Count workbench: separate collection counts and sellable-egg grading, explicit reconciliation, mortality as a separate flock event, reachable draft/submit actions.
Complete comparison tables above full-width bottom inspectors. Lists scroll independently; Products and Packed units are separate tabs on the same page.
Workflow-specific order desk, grade stock board, inventory movement ledger, production correction sheet, expense daybook, feed ticket, direct/meter water log and raw-detail reports.
Expandable Settings/Account sections and Audit rows; persistent Help contents with active-section highlighting; split Login/Set Password panels; full ZIP backup plus single-dataset CSV selector.
Login banner placement is visually approved; pre-login access to authenticated farm images remains an explicit implementation decision. Production states, localization, accessibility, tests and rebuilt-stack evidence remain acceptance work. #836's brand-mark design is separate and still open.
Slices on this epic
Formalised as an epic on 2026-09-13 during the issue cleanup — the body already said
"slices, each filed on this epic", but the epic label was missing, so nothing was navigable
from here. Label: epic-674.
Phase 3 follow-up — owner-directed redesign · phase:3-screens · preserve the completed conversion issues as history; each follow-up designs first, then implements after owner sign-off
Offline data capture (PWA) — tech spec KD-4/§6 #50 — offline data capture (PWA), KD-4. Not revamp work; independent of every phase above. (moved to its own milestone "Offline data capture (PWA)", owner 2026-09-26)
Defects
web: long tl/es strings overflow fixed-width controls on narrow screens #740 — the phone action bar cannot hold its own button labels. Added 2026-09-13 (owner)
after a live reproduction. .actions button is flex: 1 at phone width, which squeezes the button
narrow enough that its label wraps to three lines; the box then becomes taller than it is wide and border-radius: 999px resolves to an ellipse the text falls outside of. Broken in English
too — en at 420px is the worst aspect ratio of the three, so this is width-dependent, not a
locale bug. It belongs here because the fix is a decision about how action buttons lay out on a
phone, which is sequence step 1's layout-system question. Full measurements, screenshots at three
viewports and two candidate fixes are on the issue.
Offline data capture (PWA) — tech spec KD-4/§6 #50 — Offline data capture (PWA).(Moved to its own milestone "Offline data capture (PWA)", owner 2026-09-26.) Added to this epic 2026-09-13 (owner). The Installable PWA baseline — manifest, icons, service worker app-shell cache (split from #50) #142 PWA
baseline shipped; this is the queued-writes, conflict-resolution and sync half. It sits here
because it captures from the screens this revamp rewrites — built before the revamp lands it
would be built twice — and because the field-first phone direction (B) is the context that
makes offline capture worth having at all. Sequence it after the visual slices, not
alongside them. Tier4 until the design doc lands.
Constraints the slices inherit (from #650–#657, all shipped)
Sentence-case labels everywhere; caps survive only on nav group dividers (web/src/styles.caps.test.ts).
EmptyState (13 list screens, two variants, emptyStates.guard.test.ts) and .toolbar are reused, never rebuilt; --shadow-card is retired; the elevation guard (styles.elevation.test.ts) allow-lists exactly which selectors may cast a shadow — extend deliberately.
Count a selector's call sites before styling it (grep -rn "<class>" web/src --include='*.tsx'); zero is a legitimate, stated answer.
Removing a presentational transform makes every string it transformed a caller — read the strings in all three locales.
Every web/ change ships Vitest tests in the same PR; style facts jsdom cannot see go in a styles.*.test.ts parsing styles.css with postcss.
Coverage is a ratchet with <1 pt headroom on functions — never re-baseline to get green.
i18n is not English-first: every new string ships en/es/tl inline, machine-drafted and flagged for native review; Spanish and Tagalog run longer.
Docs in sync: GLOSSARY + Help page in the same PR when a concept appears or changes; a pure restyle owes neither and says so.
Rendered mockups (desktop 1440×1000 and phone 390×844 for A/B/C), the handoff notes and the decision log live in the driver's records (~/.claude/driver-records/cluckwork-revamp/), not in the repo; the design doc brings whatever the repo needs to keep.
Tracking issue for the SPA revamp. Owner decision recorded on #656 (2026-09-02): a combination — B's field-first phone, A's ledger desktop, C's attention strip reduced to one line on both. This issue carries the work; #656 stays the home of the three visual-identity questions, which the design doc answers.
What was decided, and why
Three rendered directions were built from the app's own seeded data (four houses, 327/298/341 collected with House C not recorded, 63,122 eggs on hand by grade, the 14-day trend, four real orders), differing in point of view rather than palette:
Chosen: phone = B, desktop = A, both = one attention line. The app has exactly two real contexts — a phone in a shed and a desk in an office — and the renders designed for each were B-phone and A-desktop. (The A-phone and C-phone renders reused desktop tables at 390px and overflowed; they were not fair renders of those directions.)
Sequence
docs/designs/— must answer SPA: visual identity — brand-derived link colour, display optical size, two-colour mark (owner decisions) #656's three questions (link colour derived from the brand, display optical size via the already-installed Interopszaxis, the two-colour mark) and the two questions nobody has asked: the IA (18-link flat sidebar in 5 groups, bottom tabs + "More" sheet on phone) and the layout system (today: everything is a white card; A has none, B has cards for houses only).specs/technical/tech_spec.md§8.1 (KD-6) prescribes them; the hand-built approach is a convention nobody decided. Evidence, candidates and the constraints a candidate must satisfy: #674 (comment). Ends as a decision record either way.The three visual-identity questions (folded in from #656, closed 2026-09-13)
#656 was closed during the issue cleanup and its questions moved here, so one decision has one
home. Its comment thread stays the record of how the direction was chosen; the substance is below.
The design doc in sequence step 1 answers all three.
1 — Links derive from the brand. The palette is Slack's, token for token:
#4a154bprimary,#1264a3link blue,#611f69press (web/DESIGN.md, adopted in #52). Blue links are a second huethat fights the "chromatic monotheism" the stylesheet header claims, and they ignore the farm accent
palettes (#149) — a forest or terracotta farm still gets Slack-blue links. Proposal:
--link: var(--brand)in light,--stat-accentin night, hover a step lighter/darker. Verify4.5:1 on
--surfacefor every palette × theme in the styles test, not by eye.2 — Optical size for display text. Inter everywhere at one optical size gives headings and stat
figures no voice.
font-variation-settings: "opsz" 32onh1,h2,.stat-valuebuys tighterdisplay cuts at zero download cost.
3 — The brand mark. A generic single-stroke egg outline that will not survive favicon and PWA
icon sizes. Wants a two-colour mark that holds up small.
Also inherited from #656: it was deliberately sequenced last among the seven look-and-feel
slices, because "the other six are defects; this one is a point of view." The other six shipped
(#653, #655, #660, #662, #663, #664). That ordering rationale still applies to the design doc —
answer the point-of-view questions deliberately, not as a side effect of a defect fix.
Current owner-approved screen directions
These descriptions supersede earlier visual targets for the named slices only. Implementation remains open.
Detailed approved compositions and captures: Dashboard, Daily Entry, CRUD, ledgers, Settings and support.
Login banner placement is visually approved; pre-login access to authenticated farm images remains an explicit implementation decision. Production states, localization, accessibility, tests and rebuilt-stack evidence remain acceptance work. #836's brand-mark design is separate and still open.
Slices on this epic
Formalised as an epic on 2026-09-13 during the issue cleanup — the body already said
"slices, each filed on this epic", but the
epiclabel was missing, so nothing was navigablefrom here. Label:
epic-674.Phase 0 — design ·
phase:0-design· gates everything belowdocs/designs/822-mui-revamp.md)Phase 1 — groundwork ·
phase:1-groundwork· all three run in parallel, after #822CssBaseline, type, spacing, elevation)style-src 'self'(found by feat(web): whole-app MUI baseline, theme policy guard and the #740 phone action rule (#823) #871: no MUI style reaches the screen until this lands; owner chose the nonce over'unsafe-inline', 2026-09-14)Phase 2 — controls ·
phase:2-controls· parallel with each other AND with phase 3NamedEntityPicker→ MUIAutocomplete(1,147 lines + 1,852 of test)Dialog+useConfirm+ the fouruseDialog*hooks → MUIDialog(~640 lines)NumberFieldand the hand-rolled tooltip positioningPhase 3 — screens ·
phase:3-screens· #829 first; it sets the conventions the rest followor these five screens get converted twice
Phase 3 follow-up — owner-directed redesign ·
phase:3-screens· preserve the completed conversion issues as history; each follow-up designs first, then implements after owner sign-offPhase 4 — identity ·
phase:4-identity· near-independent; #835 needs #823's type decisionopszaxisweb: a two-colour brand mark that survives favicon and PWA icon sizes #836 — a two-colour brand mark that survives favicon and PWA icon sizes(not planned, owner 2026-09-24)Unphased — start any time, blocked by nothing
Offline data capture (PWA) — tech spec KD-4/§6 #50 — offline data capture (PWA), KD-4. Not revamp work; independent of every phase above.(moved to its own milestone "Offline data capture (PWA)", owner 2026-09-26)Defects
web: long tl/es strings overflow fixed-width controls on narrow screens #740 — the phone action bar cannot hold its own button labels. Added 2026-09-13 (owner)
after a live reproduction.
.actions buttonisflex: 1at phone width, which squeezes the buttonnarrow enough that its label wraps to three lines; the box then becomes taller than it is wide and
border-radius: 999pxresolves to an ellipse the text falls outside of. Broken in Englishtoo —
enat 420px is the worst aspect ratio of the three, so this is width-dependent, not alocale bug. It belongs here because the fix is a decision about how action buttons lay out on a
phone, which is sequence step 1's layout-system question. Full measurements, screenshots at three
viewports and two candidate fixes are on the issue.
Offline data capture (PWA) — tech spec KD-4/§6 #50 — Offline data capture (PWA). (Moved to its own milestone "Offline data capture (PWA)", owner 2026-09-26.) Added to this epic 2026-09-13 (owner). The Installable PWA baseline — manifest, icons, service worker app-shell cache (split from #50) #142 PWA
baseline shipped; this is the queued-writes, conflict-resolution and sync half. It sits here
because it captures from the screens this revamp rewrites — built before the revamp lands it
would be built twice — and because the field-first phone direction (B) is the context that
makes offline capture worth having at all. Sequence it after the visual slices, not
alongside them. Tier4 until the design doc lands.
Constraints the slices inherit (from #650–#657, all shipped)
web/src/styles.caps.test.ts).EmptyState(13 list screens, two variants,emptyStates.guard.test.ts) and.toolbarare reused, never rebuilt;--shadow-cardis retired; the elevation guard (styles.elevation.test.ts) allow-lists exactly which selectors may cast a shadow — extend deliberately.grep -rn "<class>" web/src --include='*.tsx'); zero is a legitimate, stated answer.web/change ships Vitest tests in the same PR; style facts jsdom cannot see go in astyles.*.test.tsparsingstyles.csswith postcss.Touchpoints
Materials
Rendered mockups (desktop 1440×1000 and phone 390×844 for A/B/C), the handoff notes and the decision log live in the driver's records (
~/.claude/driver-records/cluckwork-revamp/), not in the repo; the design doc brings whatever the repo needs to keep.