fix(ui): lift the muted text tokens to WCAG AA #113

Merged
lz merged 1 commit from fix/muted-contrast into rc/sidebar-96 2026-09-04 16:53:26 +02:00
Owner

Second of four addressing sidebar-issues — the closing line, "Check text colors for visibility! as most text as well as '...' buttons may be very hard to spot, especially when washed out backgrounds are applied."

Measured, not eyeballed. It is worse than it looks.

The numbers

Contrast against the four surfaces text actually lands on — --bg, --surface, a menu's #0e0e0e, and a washed row's #162029 (a session colour at --wash-row over --surface). The wash is the harshest because it lightens the ground under already-dim text, so contrast is worst on exactly the rows that have something going on.

             bg  surface   menu   washed row
#444444    2.03    1.94    1.98   1.69   <- --muted-2 was here
#6a6a6a    3.66    3.49    3.57   3.05   <- --muted was here
#8a8a8a    5.73    5.47    5.59   4.78

--muted-2 at 1.69:1 on a washed row is below every threshold WCAG defines, including the 3:1 floor for controls — and that was the colour of the button on a session row.

The change

--muted#8a8a8a, clearing AA (4.5:1) on all four.

--muted-2 cannot follow it: a second tier that also cleared AA on a washed row would have to sit within a shade of the first, which is no longer a second tier. It takes --muted's old value and is now explicitly the tier for what you skim rather than parse.

That makes the tier boundary a real decision, so the call sites were sorted against it rather than left where they happened to be:

  • Controls off --muted-2 entirely (3:1 at rest is a floor it clears nowhere): .icobtn, WorkspaceRail's .foot-btn, and the dock tab's close button — the only way to close a panel, permanently visible on the active tab, so being near-invisible cost the most there.
  • Prose and labels → --muted: menu and palette section headers, keyboard hints, empty-state copy, RailHoverCard's field labels, SettingsPanel's notes, the provisioning detail line, InstallSkillModal's picker metadata.
  • Left on --muted-2, which the shift alone lifts from ~2:1 to ~3.5:1: the · between facts, breadcrumb separators, a file extension beside a full-contrast filename, input placeholders, non-text decoration.

Verification

Browser, ratios computed from the live computed styles rather than from the palette: .icobtn, .facts and .card-meta all read 5.47:1 on --surface, .section-title 5.73:1 on --bg.

Gates: typecheck 4897 files / 0 errors, lint clean, 120 files / 1200 tests.

Honest limit

--muted-2 stays deliberately below AA at 3.0–3.7:1. Closing that gap means collapsing to a single muted tone, which is a design decision rather than a fix — flagging it rather than making it here.

Second of four addressing `sidebar-issues` — the closing line, *"Check text colors for visibility! as most text as well as '...' buttons may be very hard to spot, especially when washed out backgrounds are applied."* Measured, not eyeballed. It is worse than it looks. ## The numbers Contrast against the four surfaces text actually lands on — `--bg`, `--surface`, a menu's `#0e0e0e`, and a washed row's `#162029` (a session colour at `--wash-row` over `--surface`). The wash is the harshest because it *lightens* the ground under already-dim text, so contrast is worst on exactly the rows that have something going on. ``` bg surface menu washed row #444444 2.03 1.94 1.98 1.69 <- --muted-2 was here #6a6a6a 3.66 3.49 3.57 3.05 <- --muted was here #8a8a8a 5.73 5.47 5.59 4.78 ``` `--muted-2` at **1.69:1** on a washed row is below every threshold WCAG defines, including the 3:1 floor for controls — and that was the colour of the `⋯` button on a session row. ## The change `--muted` → `#8a8a8a`, clearing AA (4.5:1) on all four. `--muted-2` cannot follow it: a second tier that also cleared AA on a washed row would have to sit within a shade of the first, which is no longer a second tier. It takes `--muted`'s old value and is now explicitly the tier for what you *skim* rather than parse. That makes the tier boundary a real decision, so the call sites were sorted against it rather than left where they happened to be: - **Controls off `--muted-2` entirely** (3:1 at rest is a floor it clears nowhere): `.icobtn`, `WorkspaceRail`'s `.foot-btn`, and the dock tab's close button — the only way to close a panel, permanently visible on the active tab, so being near-invisible cost the most there. - **Prose and labels → `--muted`**: menu and palette section headers, keyboard hints, empty-state copy, `RailHoverCard`'s field labels, `SettingsPanel`'s notes, the provisioning detail line, `InstallSkillModal`'s picker metadata. - **Left on `--muted-2`**, which the shift alone lifts from ~2:1 to ~3.5:1: the `·` between facts, breadcrumb separators, a file extension beside a full-contrast filename, input placeholders, non-text decoration. ## Verification Browser, ratios computed from the **live computed styles** rather than from the palette: `.icobtn`, `.facts` and `.card-meta` all read **5.47:1** on `--surface`, `.section-title` **5.73:1** on `--bg`. Gates: typecheck 4897 files / 0 errors, lint clean, 120 files / 1200 tests. ## Honest limit `--muted-2` stays deliberately **below AA** at 3.0–3.7:1. Closing that gap means collapsing to a single muted tone, which is a design decision rather than a fix — flagging it rather than making it here.
fix(ui): lift the muted text tokens to WCAG AA
All checks were successful
ci / nexus (pull_request) Successful in 9m13s
pr-image-cleanup / delete-pr-images (pull_request) Successful in 10s
ci / images (pull_request) Successful in 11m31s
e9cf2ffa9a
--muted-2 measured 2.03:1 on --bg and 1.69:1 on a washed row — below every
threshold WCAG defines, including the 3:1 floor for controls, which is what
the ⋯ button on a session row was using. --muted was no better than 3.66:1 /
3.05:1, so most secondary text in the app failed AA as well. The wash makes it
worse rather than better: it LIGHTENS the ground under already-dim text, so
contrast is worst on exactly the rows that have something going on.

Measured against the four surfaces text actually lands on (--bg, --surface, a
menu's #0e0e0e, and a washed row's #162029):

              bg  surface   menu   washed
    #444444  2.03    1.94   1.98   1.69
    #6a6a6a  3.66    3.49   3.57   3.05
    #8a8a8a  5.73    5.47   5.59   4.78

--muted -> #8a8a8a clears AA on all four. --muted-2 cannot follow it: a second
tier that also cleared AA on a washed row would have to sit within a shade of
the first, so it takes --muted's old value and is now explicitly the tier for
what you skim rather than parse.

That makes the tier boundary a real decision, so the call sites were sorted
against it rather than left where they happened to be:

- CONTROLS off --muted-2 entirely (3:1 at rest is a floor it clears nowhere):
  .icobtn, WorkspaceRail's .foot-btn, and the dock tab's close button — which
  is the only way to close a panel and is permanently visible on the active
  tab, so being near-invisible cost the most there.
- Prose and labels to --muted: menu and palette section headers, keyboard
  hints, empty-state copy, RailHoverCard's field labels, SettingsPanel's notes,
  the provisioning detail line, InstallSkillModal's picker metadata.
- Left on --muted-2, which the shift alone lifts from ~2:1 to ~3.5:1:
  the `·` between facts, breadcrumb separators, a file extension beside a
  full-contrast filename, input placeholders, and non-text decoration.

Verified in a browser against a seeded instance, ratios computed from the live
computed styles rather than from the palette: .icobtn, .facts and .card-meta
all read 5.47:1 on --surface, .section-title 5.73:1 on --bg.
lz merged commit 71321d6d46 into rc/sidebar-96 2026-09-04 16:53:26 +02:00
Sign in to join this conversation.
No reviewers
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lz/agent-nexus!113
No description provided.