fix(a11y): keep message-bubble metadata green legible on colored bubbles#5984
fix(a11y): keep message-bubble metadata green legible on colored bubbles#5984jamesarich wants to merge 2 commits into
Conversation
Review feedback (design#113): the green SNR/RSSI text and the signed shield sit on a per-node-tinted message bubble, where the shared "Status Good" green has poor contrast (worst in light mode) — an accessibility/readability problem. The black message text is fine because it derives from the bubble's content color; the green metadata did not. Fix is scoped to the bubble — the shared StatusGreen token is used in ~15 components, almost all on the neutral theme surface where it's fine, so changing the token globally would be the wrong lever. Instead: - Add ensureContrastOn(background): nudges a color toward black/white just enough to meet WCAG AA against a given background, preserving hue (picks the higher-contrast direction, so a mid-tone bubble darkens the green rather than washing it to white). Unit-tested. - Snr/Rssi/SnrAndRssi gain an optional `background` param (default null = unchanged everywhere else — no blast radius). - MessageItem passes the composited bubble background so SNR, RSSI, and the signed shield all stay legible; shield unified onto the same contrast-adjusted StatusGreen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extends ColorContrastTest to sweep all four quality colors (green/yellow/orange/red) across the full background-lightness range, asserting WCAG AA (4.5:1) holds after ensureContrastOn — so the bubble fix is verified for every status color and every node tint, not just green on the one sample bubble. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📄 Docs staleness check — advisoryThis PR modifies user-facing UI source files but does not update any page under
Changed source files: What to check:
New page checklist (if adding a new doc page):
If this PR does not require a doc update (e.g., internal refactor, bug fix, test change), add the
|
🖼️ Preview staleness check — advisoryThis PR modifies UI composables but does not update any
Changed UI files: What to check:
Adding previews checklist:
If this PR does not require preview updates (e.g., logic-only change, non-visual refactor), add the |
|
Superseded by #5985, which takes the surface-backed approach (StatusSurface) instead of per-bubble lightness adjustment — keeps the status tokens at their true values and is reusable for the other status-color sites. Closing in favor of that direction. |
Follow-up to the XEdDSA signing UI (#5976, #5980), addressing accessibility review feedback on design#113.
Why
The green SNR/RSSI text and the signed-broadcast shield sit on a per-node-tinted message bubble. The shared "Status Good" green (
StatusGreen, green-600) has poor contrast against those bubble colors — worst in light mode, where green-on-periwinkle is muddy and hard to read. The black message text is fine because it derives from the bubble's content color; the green metadata didn't.Blast radius — why this is scoped, not a token change
StatusGreenis used in ~15 components (signal/battery indicators, node items, nav/security icons…), but almost all render on the neutral theme surface where green-600 is fine. The only place it sits on a colored background is the message-bubble metadata row. So changing the shared token globally would touch 15 healthy components to fix one — the wrong lever. This fix stays local to the bubble and leaves the token untouched.🛠️ What changed
ensureContrastOn(background, minRatio)(new, incore/uitheme): nudges a color toward black/white just enough to meet WCAG AA (4.5:1) against a given background, preserving hue. It picks the higher-contrast direction, so a mid-tone bubble darkens the green (stays green, becomes legible) rather than washing it out to near-white. Unit-tested.Snr/Rssi/SnrAndRssigain an optionalbackground: Color? = nullparam. Defaultnull= current behavior, so every other call site is unaffected (no blast radius).MessageItempasses the composited bubble background so SNR, RSSI, and the signed shield all stay legible; the shield is unified onto the same contrast-adjustedStatusGreen.🧪 Testing performed
./gradlew :core:ui:allTests --tests "*ColorContrastTest*"— passes (raises sub-AA pairs to AA, darkens rather than washes out on mid-tone bg, leaves already-contrasting colors untouched)../gradlew spotlessCheck detekt— clean../gradlew :screenshot-tests:updateDebugScreenshotTest— all references pass; the signed-message light reference regenerated to the darker, legible green.🤖 Generated with Claude Code