Skip to content

SPA: retire tracked all-caps labels outside the nav group dividers #652

Closed

Description

@mforce

Part of the SPA look-and-feel pass (design review 2026-09-02). Sibling slices: numeric typesetting, elevation hierarchy, table layout, dashboard rework, empty states, visual identity.

Problem

Seven rules in web/src/styles.css set text-transform: uppercase with tracked letter-spacing: table.data th, .badge, .stat-label, .nav-group-label, .eyebrow, .step-n, .more-group-label. Table headers at 0.68rem caps muted are the hardest text on the screen to read, and the app is used on a phone in a hen house. Caps-everywhere is also the commonest tell of a templated UI; it stops meaning anything when it is on every label.

Proposal

Keep caps only where they mark real structure — the sidebar / More-sheet group dividers (.nav-group-label, .more-group-label). Everything else goes sentence case:

  • table.data th: ~0.8rem, sentence case, weight 600, --ink rather than --muted, letter-spacing: 0.
  • .badge: sentence case, ~0.78rem, keep the tinted pill and weight.
  • .stat-label: sentence case under the figure, ~0.85rem, --muted.
  • .step-n ("Step 1"): sentence case, keep the pill.
  • .eyebrow: audit its call sites; drop the class where it labels content that does not need a label, sentence case elsewhere.

Acceptance

  • Only the two nav group-divider rules keep text-transform: uppercase — pinned by a styles unit test.
  • Badges, table headers, stat labels, step pills render sentence case in en / es / tl (translations are already sentence case; verify nothing relied on CSS to capitalise).
  • Contrast: table headers on --surface clear 4.5:1 in light and night.
  • tools/simulation/ui/ specs that match badge text by exact case still pass (Playwright getByText is case-sensitive by default — read the callers, Daily entry: require grading to reconcile sellable eggs before submit #394).
  • Screenshots before/after in the PR.

Siblings: #650 #651 #653 #654 #655 #656 #657

Activity

  1. mforce commented on Sep 2, 2026

    @mforce
    OwnerAuthor

    Heads-up from #654, merged as 396ba233: the stat-card CSS is gone.

    .stat-grid, .stat, .stat-value and .stat-label were deleted from web/src/styles.css, because the dashboard was their only renderer and #654 replaced that row with capture-status tiles. Both of your scopes list those selectors, so that part of your work is already done — please re-read your issue body against main before picking it up rather than against the version written on 2026-09-02.

    What #654 deliberately did not touch, so it is still yours:

    One thing worth knowing before you edit those files: web/src/styles.test.ts gained a guard that walks every rule whose selector reaches a dashboard surface — including a selector list like .unrelated, .capture-tile:hover — and fails on box-shadow, text-transform, transition, animation, or any colour that does not resolve through a var(--token). It is deliberately strict; if your slice needs one of those on a dashboard surface, change the guard in the same commit and say why.

  2. mforce commented on Sep 2, 2026

    @mforce
    OwnerAuthor

    Shipped in #661, merged as 28db4c75. Every acceptance criterion met.

    text-transform: uppercase now survives on exactly .nav-group-label and .more-group-label, pinned by a whole-stylesheet walk in web/src/styles.caps.test.ts — an equality, so dropping caps from a divider fails it too. table.data th moved to --ink / 600 / 0.8rem / no tracking; .badge and .step-n sentence case with their pills intact; the dead .eyebrow and .help-toc .eyebrow rules deleted (zero call sites).

    Contrast asserted across all four palettes in both modes with the repo's own contrast() helper. The tools/simulation/ui/ specs pass — all eight columnheader queries use t()/tEn() lookups and omit exact: true, so Playwright's case-insensitive name matching resolves the same header either way. Before/after screenshots are on #661.

    One thing this issue predicted and it was right: "verify nothing relied on CSS to capitalise". Two strings did. noEntryBadge and saleableYesBadge had been authored lower case at source because .badge uppercased them, so removing the transform made them render as no entry and yes — typos, in all three locales. Found by comparing before/after screenshots of the built SPA, not by any review seat, CI Playwright run, or stylesheet walk.

    Fixed in all three locales, with the Help page and glossary copies that quote the badge moved with it, and pinned by web/src/i18n/badgeCase.test.ts, which enumerates every *Badge key per locale and requires a capital.

    Note for anyone reading the reports table: LOSSES (CR/DI/DS) now renders Losses (cr/di/ds). The abbreviation is lower case at source and was only ever capitalised by CSS; es.ts and tl.ts both carry a flag for native review note about it, so it was left as-is rather than decided here.

  3. added a commit that references this issue on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions