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: retire tracked all-caps labels outside the nav group dividers #652
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.
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.
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.
Activity
mforce commentedon Sep 2, 2026
Heads-up from #654, merged as
396ba233: the stat-card CSS is gone..stat-grid,.stat,.stat-valueand.stat-labelwere deleted fromweb/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 againstmainbefore picking it up rather than against the version written on 2026-09-02.What #654 deliberately did not touch, so it is still yours:
.capture-tile,.capture-more,.meter-stack,.dash-list,.panel-wide) carry a border and--r-card, with nobox-shadowat all, so they already sit on the side of the line your issue argues for;.panel,.cardand.order-panelare untouched and still carry--shadow-card.text-transformrule was added or removed..badgestill uppercases, which is why a no-entry tile readsNO ENTRYon the merged dashboard; that is your slice's call to make.One thing worth knowing before you edit those files:
web/src/styles.test.tsgained a guard that walks every rule whose selector reaches a dashboard surface — including a selector list like.unrelated, .capture-tile:hover— and fails onbox-shadow,text-transform,transition,animation, or any colour that does not resolve through avar(--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.test(web): fold case and walk every declaration in the style guards (#…
test(web): a keyframe is not a surface that floats (#651, #652)
fix(web): capitalise the two badge labels that relied on CSS to shout (…
feat(web): elevation hierarchy and sentence-case labels (#651, #652) (#…
mforce commentedon Sep 2, 2026
Shipped in #661, merged as
28db4c75. Every acceptance criterion met.text-transform: uppercasenow survives on exactly.nav-group-labeland.more-group-label, pinned by a whole-stylesheet walk inweb/src/styles.caps.test.ts— an equality, so dropping caps from a divider fails it too.table.data thmoved to--ink/ 600 / 0.8rem / no tracking;.badgeand.step-nsentence case with their pills intact; the dead.eyebrowand.help-toc .eyebrowrules deleted (zero call sites).Contrast asserted across all four palettes in both modes with the repo's own
contrast()helper. Thetools/simulation/ui/specs pass — all eightcolumnheaderqueries uset()/tEn()lookups and omitexact: 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.
noEntryBadgeandsaleableYesBadgehad been authored lower case at source because.badgeuppercased them, so removing the transform made them render asno entryandyes— 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*Badgekey per locale and requires a capital.Note for anyone reading the reports table:
LOSSES (CR/DI/DS)now rendersLosses (cr/di/ds). The abbreviation is lower case at source and was only ever capitalised by CSS;es.tsandtl.tsboth carry aflag for native reviewnote about it, so it was left as-is rather than decided here.docs(sim): commit the dashboard screenshot, capture the palette matri…
chore(main): release 0.1.0 (#542)