Dark-only status chips (bg-*-900/50 text-*-300) are unreadable in light mode, ~20 sites #775

Closed
opened 2026-09-23 08:59:22 +00:00 by claude-bot · 1 comment
Collaborator

Found by J6's axe gate (#770, #726), which failed CI on thirteen nodes.

The pre-redesign pages use a status-chip pattern with no light-mode counterpart at all:

// PostSessionChecklist.jsx:34-35          // SessionRsvp.jsx:40-41
running:   "bg-amber-900/50 text-amber-300"   yes:   "bg-green-900/50 text-green-300"
succeeded: "bg-green-900/50 text-green-300"   maybe: "bg-amber-900/50 text-amber-300"

Bare and unconditional — not dark:-prefixed. So a translucent dark chip composites over light paper and the pale text on it lands at ratios like 1.86:1 and 1.89:1, against WCAG 2.2 AA 1.4.3's 4.5:1.

Two independent confirmations that these are genuinely unconditional rather than dark variants leaking:

  • axe prints dark: classes in its selectors when they are present (elsewhere on the same page it reports .bg-gray-200.dark\:bg-gray-800.dark\:text-gray-500). For the amber and green chips it prints none.
  • The computed style is background-color: oklab(0.393 -0.084 0.044 / 0.5) — a translucent dark green, actually applied, in light mode.

The old app has supported light mode throughout (text-gray-600 dark:text-gray-400 is the house pattern), so these chips have been wrong in light mode since long before the redesign shell existed. Nothing in the new shell is mis-setting a theme — that was my first guess and it is wrong.

Where

grep for bg-.*-900/50 finds roughly 20 sites: Admin.jsx (×8), SessionRsvp.jsx, PostSessionChecklist.jsx, CampaignDetail.jsx, CampaignAiSettings.jsx.

Fixing only the ones a journey happens to walk would be a spot fix chosen by coincidence of coverage, and would still leave SessionDetail.jsx failing on its other six nodes.

A second, different bug in the same component

bg-gray-200 dark:bg-gray-800 text-gray-500 dark:text-gray-500 has no missing variant — it simply picks a light-mode text colour too weak for its background: gray-500 on gray-200 is 3.9:1. Worth separating, because the fix is different: this one needs a darker text colour, not a light-mode background.

The thirteen nodes on /sessions/:id, measured

Ratio Foreground / background Where
1.21 #05df72 on #cad9d1 .text-green-400 in a bg-green-900/20 panel
1.34 #7bf1a8 on #f9fafb .mt-4 > p:nth-child(1)
1.86 ×2 #ffd230 on #ba9681 RSVP "Maybe" chip; a role chip
1.89 ×2 #7bf1a8 on #83a793 checklist "Done" rows
2.48 #99a1af on #f9fafb .font-normal
2.99 #7c86ff on #f9fafb .text-indigo-400
3.9 ×4 #6a7282 on #e5e7eb checklist "No record"/"Skipped"; .ml-2

Relationship to the other accessibility debt

This is distinct from #772, which is the faint-line grey (text-gray-400 dark:text-gray-600, 2.6:1) in 16 places. Same standard, different pattern, different fix. Both were found by journeys reaching pages nobody had scanned before.

Most of these sites are on surfaces lanes 4, 5 and 9 will rebuild, so in principle each lane fixes its own. The reason to look sooner: J5 walks this exact page ("after the session: the checklist, transcript and summary review"), and it will meet all thirteen nodes before lane 4 has rebuilt anything. Per #771 it will not be able to tolerate them for one page either.

🤖 Generated with Claude Code

Found by J6's axe gate (#770, #726), which failed CI on thirteen nodes. The pre-redesign pages use a status-chip pattern with **no light-mode counterpart at all**: ```js // PostSessionChecklist.jsx:34-35 // SessionRsvp.jsx:40-41 running: "bg-amber-900/50 text-amber-300" yes: "bg-green-900/50 text-green-300" succeeded: "bg-green-900/50 text-green-300" maybe: "bg-amber-900/50 text-amber-300" ``` Bare and unconditional — not `dark:`-prefixed. So a translucent **dark** chip composites over **light** paper and the pale text on it lands at ratios like 1.86:1 and 1.89:1, against WCAG 2.2 AA 1.4.3's 4.5:1. Two independent confirmations that these are genuinely unconditional rather than dark variants leaking: - axe prints `dark:` classes in its selectors when they are present (elsewhere on the same page it reports `.bg-gray-200.dark\:bg-gray-800.dark\:text-gray-500`). For the amber and green chips it prints none. - The computed style is `background-color: oklab(0.393 -0.084 0.044 / 0.5)` — a translucent dark green, actually applied, in light mode. The old app has supported light mode throughout (`text-gray-600 dark:text-gray-400` is the house pattern), so these chips have been wrong in light mode since long before the redesign shell existed. **Nothing in the new shell is mis-setting a theme** — that was my first guess and it is wrong. ## Where `grep` for `bg-.*-900/50` finds roughly 20 sites: `Admin.jsx` (×8), `SessionRsvp.jsx`, `PostSessionChecklist.jsx`, `CampaignDetail.jsx`, `CampaignAiSettings.jsx`. Fixing only the ones a journey happens to walk would be a spot fix chosen by coincidence of coverage, and would still leave `SessionDetail.jsx` failing on its other six nodes. ## A second, different bug in the same component `bg-gray-200 dark:bg-gray-800 text-gray-500 dark:text-gray-500` has no *missing* variant — it simply picks a light-mode text colour too weak for its background: gray-500 on gray-200 is **3.9:1**. Worth separating, because the fix is different: this one needs a darker text colour, not a light-mode background. ## The thirteen nodes on `/sessions/:id`, measured | Ratio | Foreground / background | Where | |---|---|---| | 1.21 | `#05df72` on `#cad9d1` | `.text-green-400` in a `bg-green-900/20` panel | | 1.34 | `#7bf1a8` on `#f9fafb` | `.mt-4 > p:nth-child(1)` | | 1.86 ×2 | `#ffd230` on `#ba9681` | RSVP "Maybe" chip; a role chip | | 1.89 ×2 | `#7bf1a8` on `#83a793` | checklist "Done" rows | | 2.48 | `#99a1af` on `#f9fafb` | `.font-normal` | | 2.99 | `#7c86ff` on `#f9fafb` | `.text-indigo-400` | | 3.9 ×4 | `#6a7282` on `#e5e7eb` | checklist "No record"/"Skipped"; `.ml-2` | ## Relationship to the other accessibility debt This is **distinct from #772**, which is the faint-line grey (`text-gray-400 dark:text-gray-600`, 2.6:1) in 16 places. Same standard, different pattern, different fix. Both were found by journeys reaching pages nobody had scanned before. Most of these sites are on surfaces lanes 4, 5 and 9 will rebuild, so in principle each lane fixes its own. The reason to look sooner: **J5 walks this exact page** ("after the session: the checklist, transcript and summary review"), and it will meet all thirteen nodes before lane 4 has rebuilt anything. Per #771 it will not be able to tolerate them for one page either. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Collaborator

Partly fixed by #793 (merged in a028f55). About twenty dark-only status chips now have an opaque light pair (bg-{color}-100 text-{color}-800) behind a dark: prefix: RSVP, the checklist, content packs, votes, recordings, wiki suggestions, and Admin's pills. The thirteen /sessions/:id nodes are fixed too, including the "Confirmed" slot, text-indigo-400 and the gray-500-on-gray-200 chips. Every pair is at least 5.5:1 in both modes.

This issue stays open for the dark-only colours the sweep didn't reach: LootLedger, QuestLog, VotingGrid, entryTypes, Admin, CampaignDetail, CampaignPlanning and the wiki pages. That work is on #794 and is being finished now.

🤖 Generated with Claude Code

Partly fixed by #793 (merged in a028f55). About twenty dark-only status chips now have an opaque light pair (`bg-{color}-100 text-{color}-800`) behind a `dark:` prefix: RSVP, the checklist, content packs, votes, recordings, wiki suggestions, and Admin's pills. The thirteen `/sessions/:id` nodes are fixed too, including the "Confirmed" slot, `text-indigo-400` and the gray-500-on-gray-200 chips. Every pair is at least 5.5:1 in both modes. This issue stays open for the dark-only colours the sweep didn't reach: LootLedger, QuestLog, VotingGrid, entryTypes, Admin, CampaignDetail, CampaignPlanning and the wiki pages. That work is on #794 and is being finished now. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No milestone
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
rbrooks/Quest-Board#775
No description provided.