Skip to content

web: visual language for the MUI revamp, chosen and encoded as theme overrides #864

Closed

Description

@mforce

Part of #674. Groundwork, sequenced after #823 and before any control or screen converts.

Why this slice exists

The revamp's point was a nicer UI. The milestone decides the point of view (ledger desktop, field-first phone, one attention line) and three identity fixes (#834, #835, #836), but nothing owns the visual language itself. docs/decisions/674-ui-component-library.md says the accepted cost of MUI is that it "renders as a Google app until deliberately restyled; that work is the revamp's", and no slice picks that up. The design doc (#822, D6) ports today's font sizes into MUI variants, adopts MUI's 8px spacing and keeps the 6/10/16 radius scale. That is faithful to today, not nicer. Without this slice every screen conversion inherits the default Material look, and restyling afterwards means touching every screen twice.

Scope

Decide, in one place, what Cluckwork looks like on MUI, and encode it as theme overrides in web/src/theme/ so #826 onward inherit it:

Out of scope: any screen conversion, the mark (#836), offline (#50), a chart library.

Step 1: mockup ideas

Two or three rendered directions of the Dashboard and Daily entry at 1280 and 390, built from the app's own seeded data (four houses 327/298/341 with House C not recorded, 63,122 eggs on hand by grade, the 14-day trend, four real orders), in the default aubergine palette and one other, light and night. Static HTML is fine; the point is to look, not to ship. The owner picks a direction or a combination, as on #656. Attach them to this issue with gh issue comment --attach, never committed to a branch.

Done when

Constraints inherited

Sentence case everywhere (caps only on nav group dividers); elevation encodes what floats (#651); --shadow-card stays retired; the four palettes and both modes must keep 4.5:1 on every text pairing (styles.test.ts); Spanish and Tagalog run longer, so nothing is sized to the English string; precache ceiling per #825.

Activity

  1. added this to the SPA revamp milestone on Sep 14, 2026
  2. added
    sliceThin vertical work item
    epic-674SPA revamp — field-first phone, ledger desktop (#674)
    phase:1-groundworkWhole-app decisions before any screen converts
    on Sep 14, 2026
  3. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Mockup direction 1: Ruled paper

    The farm's ruled record book: numerals are the loudest thing on screen and every other mark is a hairline keeping them in line. Keeps the aubergine sidebar. Status is a dot plus a word, never a filled chip. The trend chart truncates its axis at 800 (the app starts at zero); that is a choice to accept or reject.

    Rendered from static HTML with the app's real palette tokens and seeded numbers, at 1280x800 and 390x844, in the default aubergine palette (light) and the forest palette (night). The trend chart follows the current app's bar strip (#781), which the first drafts had missed because the committed docs/images/dashboard.png predates it.

    Direction 1, dashboard at 1280, aubergine light
    Direction 1, dashboard at 1280, forest night
    Direction 1, dashboard at 390, aubergine light
    Direction 1, daily entry at 390, aubergine light

    Direction notes

    Ruled paper

    Cluckwork becomes the farm's ruled record book, where the numbers are the loudest thing on the screen and every other mark is a hairline whose only job is to keep them in line.

    Type. Inter, six steps and nothing between them. 11.5 micro (units, nav dividers, share percentages), 13 label, 15 body, 17 ledger numeral, 22 total, 26 page title, plus one 28 display numeral for the phone house tiles, the stock total and the hen-day figure. Numerals are always tabular at weight 600, so a column of counts aligns by digit and a changed digit is the only thing that moves. Weight stops at 700. The current 800 with -0.02em tracking is gone, and only the title and the tile numeral carry -0.015em. Optical sizing is left to Inter's opsz axis through font-optical-sizing: auto.

    Colour. Every colour token is copied unchanged from web/src/styles.css. What this direction changes is the budget. Brand appears in four places, the nav rail, the primary button, the active nav marker and the day strip bars, and nowhere else, so a ledger row never carries brand. A grade is identified by a 10px square in its --grade-N hue, and the five shares are carried once by a single 10px stacked rule rather than five separate bars, so grade colour never becomes a filled block that reads as a status. Status is a dot plus a word in --success, --warn, --muted or --error, and there is no filled status pill anywhere. Night adds two derived tokens, --sheet and --float, because --surface is the one dark token the four palettes do not re-scope, and leaving it in place puts a plum sheet under a forest rail. In night the primary button takes a 1px --stat-accent edge, since the brand fill is mode independent and goes flat against a dark surface.

    Shape. Radius rises only with elevation. Rows and rules are 0, controls and marks are 3px, and 14px is reserved for things that float, which here means the phone house tiles, dialogs and sheets. Pill buttons are gone. Inputs and buttons are the same 3px rectangle, so a stepper reads as one instrument rather than three parts. A total closes with a 3px double rule, the way a ledger closes a column. The day strip follows the same logic, a column per day on a 4px gap with a 9px break between the two weeks, an average drawn as a dashed rule across the bars, and a partial day drawn as a 3px floor rather than a full bar.

    Density. 1280 is a 40px row, a 216px rail, a 1fr plus 320px split with a hairline between the columns, and no card anywhere. 390 is a 56px row, a 44px minimum target, 64px stepper buttons, full width everything, and cards only for the four house tiles. A row action is ruled text at 1280 and a 44px outlined button at 390, which is how "Kumpirmahin ang order (maglalaan ng stock)" survives both widths without anything being sized to an English string. The attention line stays one line at both widths, and at 390 only the first item survives while the rest fold into the count.

    Rejected. Cards on the desktop dashboard, which is what ships today and what makes four houses look like eight panels. Filled status pills, which spend the most saturated colour on the words that need help least. Brand-tinted headers and rows, which put the farm's palette in competition with the grade hues on the one screen that shows both. A shadow on anything static. An 800 weight. Icons in ledger rows. A second chart, because one 14 day strip already answers the question the dashboard asks. A line across those 14 days, because a daily count has no values between two days, and the strip can show an unfinished day as a floor where a line can only draw it as a dip.

  4. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Mockup direction 2: The ruled ledger, brand as a spine

    White paper ruled with hairlines. The brand is a thin spine (a tinted nav rail instead of the aubergine slab, the active item's rule, one primary button, the focus ring), never a block. Underline inputs instead of boxes, pills retired, tabular numerals first. The one fill on the phone dashboard is the missing house's tile.

    Rendered from static HTML with the app's real palette tokens and seeded numbers, at 1280x800 and 390x844, in the default aubergine palette (light) and the forest palette (night). The trend chart follows the current app's bar strip (#781), which the first drafts had missed because the committed docs/images/dashboard.png predates it.

    Direction 2, dashboard at 1280, aubergine light
    Direction 2, dashboard at 1280, forest night
    Direction 2, dashboard at 390, aubergine light
    Direction 2, daily entry at 390, aubergine light

    Direction notes

    Direction: the ruled ledger, brand as a spine

    One sentence: the working screen is white paper ruled with hairlines, the brand colour is a thin spine (nav rail tint, active item rule, one primary button, focus ring) and never a slab, and every number is a tabular numeral first.

    Type scale (Inter, optical size follows font-size)

    • Display numeral 40/44, weight 600, tracking -0.02em. Used for one number per section (today's total, eggs on hand). Phone tiles 32/36.
    • Page title 24/28, weight 600, tracking -0.015em. Phone 28/32. Weight 800 is retired; the before page shouts.
    • Section head 13/16 weight 600, sentence case, ruled beneath with a stronger line. Phone 15/20.
    • Row text 14/20 desktop, 16/24 phone. Numerals in rows weight 500, tabular, right aligned. Field numerals 20/24 desktop, 28/32 phone.
    • Caption 12/16 muted. Nav group divider 11/16, weight 600, 0.08em tracking, the only uppercase on the product.

    Colour rules

    • The nav rail is --lavender (the palette's tinted paper) with --stat-accent text, not a filled brand block. A forest farm reads green through the tint and the accent; the slab was the Slack tell.
    • Brand appears in exactly four places on a working screen: farm name, active nav item (2px left rule plus text), the primary button fill, the focus ring. Nothing else is brand coloured.
    • Links are ink, underlined in a 28% ink rule, full ink on hover. --link blue leaves the data screens; it stays for prose (help).
    • Status is a word with an 8px dot before it. Dot colour is the whole vocabulary: success (paid, submitted), --stat-accent (allocated, in progress), hollow ring (draft), --warn (not recorded, low). No tinted badge fills.
    • One fill exists: the phone House tile for a missing entry takes --tint-warn with a --warn border, because it is the one thing on the screen asking for a thumb.
    • Grade hues appear only in the stock meter and its 10px swatches. Never on a grade name in a form.
    • Two rule weights: --rule (the hairline) between rows, and --rule-strong (28% ink) under headers, above totals, and on underline fields. Dark mode inherits both from the tokens; no new dark values.

    Shape and density

    • Controls 4px radius, tiles 8px, dialogs 12px. Pills are gone; a pill button on a ledger reads as marketing.
    • Inputs have no box. An underline field, 1px strong rule, 2px focus rule. Selects the same. Stepper buttons are 36px squares with a hairline border at 1280 and 48px at 390.
    • Desktop rows 36px, 8px vertical padding, no card, no panel, no background. Sections sit side by side across a single vertical hairline.
    • Phone rows 52px minimum (64px with a stepper), every target 44px or larger, buttons 48px, the action bar splits into two equal 48px buttons above the 64px tab bar.
    • Elevation: --shadow-bar on the sticky action bar and the tab bar, --shadow-dialog on dialogs. Nothing else casts.
    • Last 14 days is a strip of 14 bars in --stat-accent from a zero baseline, 4px apart with a 9px week break, a dashed ink average across them, Avg and Peak in the header, the date range under the rule, hen-day as the section's display figure with its delta in success or warn. The dip day is --warn, a partial day is hatched, and a hover or focus readout sits in a reserved 28px row so nothing reflows. At 390 the 14 bars are 21px wide, the one target under 44px on the product; the strip is read, not operated.
    • The attention line is one flex line under the title: 8px mark, text, optional link. Quiet state is muted with a hollow mark; one thing is a warn mark; several leads with a bold count ("3 things.") and middle dots, truncates on desktop, wraps on phone.
    • Long labels wrap inside their control (white-space: normal on every button); the Tagalog confirm button sits in a ledger cell at 1280 and becomes a full width 48px button at 390.

    Rejected

    • Keeping the aubergine sidebar slab: it is the one thing that makes every palette look like Slack in a costume.
    • Cards for stats and stock: the ledger says the screen is the record, and a card says the screen is a poster.
    • Coloured badges for status and colour on grade names: two chromatic systems next to each other read as one, which the grade hue guard already had to police.
    • Material defaults: 12px radius, filled inputs, elevated app bar, ripple. Adopted MUI is the engine; none of its look survives.
  5. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Mockup direction 3: The quiet ledger

    Ink and rule lines carry the hierarchy. Brand colour appears in exactly two places per screen (active nav mark, primary action) so a farm's palette reads as identity, not decoration. Keeps the aubergine sidebar, puts the numeral first in every row, and reserves radius for controls; a ledger row is flat and hairline-divided.

    Rendered from static HTML with the app's real palette tokens and seeded numbers, at 1280x800 and 390x844, in the default aubergine palette (light) and the forest palette (night). The trend chart follows the current app's bar strip (#781), which the first drafts had missed because the committed docs/images/dashboard.png predates it.

    Direction 3, dashboard at 1280, aubergine light
    Direction 3, dashboard at 1280, forest night
    Direction 3, dashboard at 390, aubergine light
    Direction 3, daily entry at 390, aubergine light

    Direction notes

    Direction: the quiet ledger

    Ink and rule lines carry the hierarchy; brand colour is spent on exactly two
    things per screen (the active nav mark, the one primary action), so a
    farm's palette reads as identity, not decoration.

    Type scale (Inter, static weights 400/500/600/700/800)

    • Display, page title only: 26px/1.15, weight 800, tracking -0.02em.
    • Ledger numeral, the subject of every row: 21-28px/700, tabular figures.
    • Section head 15px/600, body/row label 14px/400-600, meta 11.5-12.5px muted.
    • No caps except nav dividers; badges and buttons stay sentence case.

    Colour rules

    • Brand fill appears twice: active nav mark, primary CTA. Everything else is ink, muted, or a hairline.
    • Status (missing entry, paid/draft/allocated, the trend's dip) uses
      semantic tokens only, never brand, so it cannot read as "the farm's colour."
    • Grade identity is a swatch and meter fill beside the name, never a row or
      card background, so it cannot read as a status colour either.
    • Every colour token is copied verbatim from web/src/styles.css; only the
      shape tokens are this direction's own choice.

    Shape and density

    • Radius is reserved for controls: inputs, steppers, buttons, cards,
      dialogs. A ledger row is flat and hairline divided, never rounded.
    • Desktop: rows run 40-44px tall, numeral column fixed-width right of the
      label so figures align down the page.
    • Phone: house tiles are the one card surface, dashboard only; the rest,
      and all of daily entry, is full-width rows with 40-44px steppers. The
      sticky bar and tab bar are the only shadows.
    • The attention line has three states, two shown live, one per page:
      "nothing to say" (daily entry, plain text) and "one thing" (dashboard,
      warn dot). "Several" is the same line plus a count, left undemonstrated
      rather than inventing houses.

    Rejected

    • Coloured KPI tiles, the Material default this arena exists to avoid.
    • Icon-led navigation: text carries more per pixel than a glyph to learn.
    • Shadows on cards and rows: elevation is for things that actually float.
    • A second accent colour: one brand hue plus neutrals survives all four farm palettes with no per-farm re-tuning.
  6. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Direction pick (provisional, 2026-09-14). Owner leans direction 2 ("the ruled ledger, brand as a spine"); the agent recommends it too: it is the only direction that removes the aubergine slab, the one element that makes every palette look like Slack in a costume, and its tinted rail carries the farm palette as identity in both modes (forest night render above holds up). Brand colour in four places only; underline inputs; pills retired; status as dot plus word; the phone's single fill goes to the missing house.

    Two grafts to carry into the theme work: direction 3's numeral-first rows for the Today list at 1280, and direction 1's full-row amber rule for the not-recorded house. One caution: underline fields have a smaller visible target than boxed ones; the CRUD dialogs' text fields keep a visible field surface at 390 so a gloved hand does not miss them. The trend chart follows the app's bar strip (#781) as it already does in the renders.

    Owner: reply "confirmed" here (or name changes) and the theme overrides get built from this.

  7. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Owner direction (2026-09-14): a mix rather than one candidate. Dashboard structure and controls from direction 1, colours from direction 2; daily entry, the phone menu and the phone daily entry as direction 2 does them. A combined mockup is being built and will be attached here for confirmation before any theme work starts.

  8. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Combined mockup: direction 1's dashboard structure in direction 2's colours

    Per the owner's mix (dashboard controls of 1, colours of 2, daily entry and phone menu of 2). Same real tokens, same seeded numbers, zero-baseline bar strip. Daily entry and the phone shell are direction 2's unchanged, so their renders are the ones on the direction 2 comment above; the two new views are the dashboard at 1280 (light aubergine, night forest) and at 390.

    Combined, dashboard at 1280, aubergine light
    Combined, dashboard at 1280, forest night
    Combined, dashboard at 390, aubergine light
    Combined, daily entry at 390, aubergine light

    Direction notes

    Combined direction: candidate 1's dashboard bones in candidate 2's colour and type

    One sentence: the dashboard keeps candidate 1's structure and controls (Today rows with recorded times and the amber full-row rule on the missing house, sales ledger with the order number under the customer, Eggs on hand and Last 14 days in a right column, an attention line that folds into a count) and everything it wears comes from candidate 2, whose daily entry and phone shell are used unchanged.

    From candidate 1 (structure, controls)

    • Today is a ruled list: name, entry state with its time, action, count; the unrecorded house carries a 3px warn rule down its left edge; a double rule closes the total.
    • A row action is ruled text at 1280 and a full width 44px outlined button at 390. One affordance, two densities.
    • Two columns at 1280: Today and Recent sales left, Eggs on hand and Last 14 days in a 320px right column across a hairline.
    • Recent sales rows: customer with the order number under it, eggs and grade, peso amount, status, action.
    • The attention line never wraps: at 1280 two items fit and the rest fold into "+N more"; at 390 one item fits.
    • At 390 the houses are 2x2 cards, the one card surface on the product, with the missing house tinted and carrying its own button.

    From candidate 2 (colour, type, rules, daily entry, phone shell)

    • Nav rail is --lavender tinted paper with --stat-accent text, not a brand slab. Brand appears in four places: farm name, active nav item (2px rule plus text), primary button, focus ring.
    • Status is a word with an 8px dot: success (recorded, paid), --stat-accent (allocated), hollow ring (draft), --warn (not recorded, low). No badge fills. Grade hues only in the stock meter and swatches. Links are ink, underlined in a 28% ink rule.
    • Type: display numeral 40/44 weight 600 (one per section: eggs on hand, hen-day), title 24/28 (phone 28/32), section head 13/16 ruled, rows 14/20 desktop and 16/24 phone with weight 500 tabular numerals, caption 12/16, the nav group divider the only uppercase.
    • Two rule weights: --rule between rows, --rule-strong (28% ink) under headers, above totals, under fields. Controls 4px radius, cards 8px, dialogs 12px. Elevation only on the action bar, the tab bar and dialogs.
    • Desktop rows 36px; phone rows 52px minimum, every target 44px or more, buttons 48px, the phone tab bar with icons and the phone daily entry exactly as candidate 2 built them.
    • Last 14 days: 14 bars from a zero baseline in --stat-accent, 4px apart with a 9px week break, a dashed ink average across them, Avg and Peak in the header, the date range under the rule, hen-day as the display figure with its delta. The dip day is --warn, today is hatched as a partial floor, the readout sits in a reserved 28px row and the dip day is selected by default so the render shows it. At 390 the bars are 21px, the one target under 44px; the strip is read, not operated.

    Rejected

    • Candidate 1's aubergine rail slab, 800-floor chart scale and blue link colour.
    • Candidate 2's original single-column Today table and its tile-as-link on the phone; candidate 1's per-row control is clearer.
    • Material defaults: filled inputs, 12px radius everywhere, elevated app bar, badge chips.

    Owner: reply "confirmed" to make this the #864 direction, or name what to change.

  9. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Owner feedback on the combined mockup (2026-09-14): the phone dashboard cards are rejected, and the trend panel should follow direction 1's chart. Revision in progress: phone Today becomes a ruled list (with a compact-tile variant rendered alongside so the owner can choose), and the Last 14 days panel takes direction 1's presentation. Note for the eventual slice: direction 1's chart uses a non-zero floor ("scale from 800") where the app's strip is zero-based today (#780); adopting that presentation is a product decision the slice records.

  10. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Combined mockup, revision 2

    Per the owner: direction 1's chart in the trend panel (with its 800 floor, kept as drawn; the app's strip is zero-based today, #780, so adopting the floor is a product call for the slice), and the phone dashboard cards replaced. Two phone variants for the owner to choose between: a ruled list, and compact tiles with no buttons where the whole tile is the tap target.

    Combined rev 2, dashboard at 1280, aubergine light
    Combined rev 2, phone Today as a ruled list
    Combined rev 2, phone Today as compact tiles

    Owner: name the phone variant (list or tiles) and say "confirmed", or name what else to change.

  11. mforce commented on Sep 14, 2026

    @mforce
    OwnerAuthor

    Direction confirmed (owner, 2026-09-14): combined mockup revision 2, phone Today as the ruled list. Direction 1's dashboard structure and chart (800 floor kept as drawn, product call recorded for the slice), direction 2's colours, type, rules, daily entry and phone shell. The mockup HTML and its DIRECTION.md are committed with the slice as the design authority, per the discount-mockups precedent. Theme work starts now: #823's baseline first, then this issue's overrides on top, each with before/after captures.

  12. 8 remaining items

  13. mforce commented on Sep 16, 2026

    @mforce
    OwnerAuthor

    Owner pick (2026-09-16), desktop: all three rendered changes are in for the slice: the single filled Record House C button at 1280 (amends the ruled-text sentence in DIRECTION.md), the Yesterday by close reference under Today so far, and the ruled separators in the attention line. The desktop-only Needs attention list is not taken; it stays a note here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:frontendReact/Vite web clientepic-674SPA revamp — field-first phone, ledger desktop (#674)phase:1-groundworkWhole-app decisions before any screen convertspriority:tier2High value, low risksliceThin vertical work item

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions