id: b085165e-9006-4ef4-a85f-ad58b2946280 title: Light theme retune + AA contrast pass (--muted is 2.76:1 dark / 4.26:1 light) state: closed labels:
- ui assignees: [] milestone: null created: 2026-10-27T15:28:20.051026328Z updated: 2026-10-27T16:00:02.615871182Z
The dark theme (Tokyo Night) is the designed one and reads well. The light theme is the weak one: page #d5d6db, header #b4b8c7, body #343b58 on a dusty blue-grey field — it looks like a 2010s admin panel, everything on one flat tone with no surface separation. It is also the theme most first-time visitors on desktop will get.
Measured contrast (WCAG):
| pair | ratio | |
|---|---|---|
dark --fg #c0caf5 on --bg #1a1b26 | 10.59:1 | ok |
dark --muted #565f89 on --bg #1a1b26 | 2.76:1 | fails AA |
dark --muted on --bg-alt #16161e | 2.91:1 | fails AA |
light --fg #343b58 on --bg #d5d6db | 7.57:1 | ok |
light --muted #565f89 on --bg #d5d6db | 4.26:1 | fails AA |
light --muted on --bg-th #b4b8c7 | 3.13:1 | fails AA |
light --blue link #34548a on --bg | 5.20:1 | ok |
--muted is used for all small text: footer/provenance, meta lines, breadcrumbs, line numbers, comment authors, "from .forge/wiki/README", diff index lines. It is the most-used color after --fg and it is the least legible.
Scope
- Light palette: move to a real light product palette —
--bg:#f6f7f9(or white),--surface:#ffffff,--surface-2:#eef0f4,--bg-alt:#eceef2header,--border:#d6dae2, keep--fgdark and--blue/--cyandeep enough for AA. The point is surface separation: content panels sit on a lighter surface than the header/background. --mutedsplit: introduce--fg-dimthat passes 4.5:1 in both themes (e.g. dark#9aa5ce-ish is 7.5:1; light#5a6383~5:1) and use it for body-adjacent meta; keep the current--mutedonly for non-essential decoration, if at all.- Re-check every token used for text: state pills, badge colors (
--green/--red/--yellowin light mode are currently dark-on-light and probably pass, verify), diff add/del backgrounds, token colors from the highlighting issue. color-schemealready declaresdark light; keep respecting the OS preference (no JS toggle) but make both themes first-class.- Add a tiny test asserting the palette values stay in sync between
:rootand the light media query (both must define every token), plus a contrast helper test for--fg/--fg-dim/link colors against their backgrounds.
Acceptance
- every color used for text passes 4.5:1 in both themes (computed in a test, not by eye)
- light theme has visible surface separation (header ≠ page ≠ card)
-
no hardcoded hex outside
:root/media query in new rules - screenshots at 1280/390 in both themes attached to the PR