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
Apply the same 2-surface / minimal-token collapse to light mode that PR #834 (eab4cbfb78) shipped for dark, so the system has a single coherent token contract across both schemes. Light's pain level is lower (no "foggy" perception issue like dark Settings 1.14:1), so this is system-health work rather than a user-visible fix — keep at P2.
Current state (audit 2026-05-22 against packages/ui/src/styles/theme.css light block, L83-164)
--bg-base and --surface-base both #fff (already aliased), but --sidebar #f9f9f9 and --surface-list-hover #f4f0ec each invent their own off-white. Comment says "2 tiers" but the value count is 4.
Highest-severity finding: the same "weak border" looks different against #ffffff vs #faf9f7 because two tokens are opaque hex while the base is alpha. Dark mode collapsed all three to a single alpha.
Comment in theme.css says selected vs list-hover hue split is intentional, but list-hover hex is essentially a desaturated cream of the same neighborhood.
Icon
4 values, --icon-disabled #c7c7c7 is not derived from fg-*
Other icon tokens mirror fg-strong / fg-weak / fg-weaker; icon-disabled invented its own neutral grey.
raised (#faf9f7 or new mid value) — --surface-sunken, --bg-cream, --sidebar, --surface-list-hover
fg (2 tiers) — collapse 4 grays to strong + weak per dark pattern
border (single alpha) — rgba(0, 0, 0, 0.10-0.16), drop the opaque #ece6df / #f3ede6 variants
interactive cream — keep #fdf6f0 for selected (brand-tinted identity), drop the second cream hover layer
Why this is P2 and not P0
Dark mode had a real "foggy" / 1.14:1 Settings dissolve user pain (see closed #617). Light doesn't — #ffffff background gives huge perceptual headroom and the border-form inconsistency hides because most users never put two surface tiers next to each other.
But the system contract is asymmetric today: dark is 5 tokens, light is 12+. Future visual work (#583 Context tab, #823 Turn Changes visual) will keep paying the asymmetry tax until light is collapsed too.
Scope when picked up
In scope:
packages/ui/src/styles/theme.css light block (L83-164) and the corresponding pawwork.json light overrides
packages/ui/test/theme-parity.test.ts — keep green; update the contract if collapse forces shape changes
Hardcoded light hex in callsites that don't pull from CSS vars (audit similar to the dark side: packages/app/src/index.css desktop shell, theme-color meta tags, switch thumb, message-part user bubble)
Snap regression: the new session-turn-changes / sidebar / sidebar-unread / app-shell targets already capture light shots; before/after diffs go in the PR
Out of scope:
Reference-product survey + handoff doc + prototype HTML — the dark redesign used those because it had a real perception problem. Light is system-health; the cleanup pattern can be lifted straight from dark.
--shell-border-{base,weak} in packages/app/src/index.css are color-mix chrome inputs (titlebar + frame), not body dividers. They kept two values in dark; light has the same shape (#D6D2CA + #E3DED6). Keep that exemption in light too with a parallel comment.
The user-bubble re-identification trade-off in dark (1.46:1 → 1.15:1, mitigated by right-align + asymmetric corner radius) does not need to repeat for light because the light --surface-interactive-base #fdf6f0 is brand-tinted, not neutral cream — keeping its identity through hue, not luminance.
How to verify (once a PR exists)
bun --cwd packages/ui test theme-parity → light overrides match bun --cwd packages/ui x tsgo --noEmit and bun --cwd packages/app x tsgo --noEmit → clean bun run snap app-shell, bun run snap sidebar, bun run snap session-turn-changes, bun run snap sidebar-unread → light halves of each grid should still read coherently; before/after PNGs in the PR body
Goal
Apply the same 2-surface / minimal-token collapse to light mode that PR #834 (
eab4cbfb78) shipped for dark, so the system has a single coherent token contract across both schemes. Light's pain level is lower (no "foggy" perception issue like dark Settings 1.14:1), so this is system-health work rather than a user-visible fix — keep at P2.Current state (audit 2026-05-22 against
packages/ui/src/styles/theme.csslight block, L83-164)#ffffff/#faf9f7/#f9f9f9/#f4f0ec--bg-baseand--surface-baseboth#fff(already aliased), but--sidebar #f9f9f9and--surface-list-hover #f4f0eceach invent their own off-white. Comment says "2 tiers" but the value count is 4.#1a1613/#4a4138/#8c817a/#b6ada7/#fffffffg-basevsfg-weakreads close in body copy size.rgba(0,0,0,0.162)(--border-base) vs#ece6df/#f3ede6(--border-weak/--border-weaker)#ffffffvs#faf9f7because two tokens are opaque hex while the base is alpha. Dark mode collapsed all three to a single alpha.#fdf6f0(selected) /#fbece0(hover) /#f4f0ec(list hover)--icon-disabled #c7c7c7is not derived fromfg-*fg-strong/fg-weak/fg-weaker;icon-disabledinvented its own neutral grey.Target after collapse (proposed, not committed):
#ffffff) —--bg-base,--surface-base,--surface-raised#faf9f7or new mid value) —--surface-sunken,--bg-cream,--sidebar,--surface-list-hoverrgba(0, 0, 0, 0.10-0.16), drop the opaque#ece6df/#f3ede6variants#fdf6f0for selected (brand-tinted identity), drop the second cream hover layerWhy this is P2 and not P0
Dark mode had a real "foggy" / 1.14:1 Settings dissolve user pain (see closed #617). Light doesn't —
#ffffffbackground gives huge perceptual headroom and the border-form inconsistency hides because most users never put two surface tiers next to each other.But the system contract is asymmetric today: dark is 5 tokens, light is 12+. Future visual work (#583 Context tab, #823 Turn Changes visual) will keep paying the asymmetry tax until light is collapsed too.
Scope when picked up
In scope:
packages/ui/src/styles/theme.csslight block (L83-164) and the correspondingpawwork.jsonlight overridespackages/ui/test/theme-parity.test.ts— keep green; update the contract if collapse forces shape changespackages/app/src/index.cssdesktop shell,theme-colormeta tags, switch thumb, message-part user bubble)session-turn-changes/sidebar/sidebar-unread/app-shelltargets already capture light shots; before/after diffs go in the PROut of scope:
Notes from the dark side
--shell-border-{base,weak}inpackages/app/src/index.cssarecolor-mixchrome inputs (titlebar + frame), not body dividers. They kept two values in dark; light has the same shape (#D6D2CA+#E3DED6). Keep that exemption in light too with a parallel comment.--surface-interactive-base #fdf6f0is brand-tinted, not neutral cream — keeping its identity through hue, not luminance.How to verify (once a PR exists)
bun --cwd packages/ui test theme-parity→ light overrides matchbun --cwd packages/ui x tsgo --noEmitandbun --cwd packages/app x tsgo --noEmit→ cleanbun run snap app-shell,bun run snap sidebar,bun run snap session-turn-changes,bun run snap sidebar-unread→ light halves of each grid should still read coherently; before/after PNGs in the PR body