bug(a11y): low-contrast secondary text fails WCAG AA across the app #92

Closed
opened 2026-03-27 12:19:35 +01:00 by marcel · 0 comments
Owner

Severity: High — accessibility failure (WCAG SC 1.4.3)

Multiple instances of secondary/helper text render in a gray that fails the 4.5:1 contrast ratio required for normal-weight text on a white or near-white background.

Affected elements (light mode):

Element Current approx. color Contrast on white Required
"Forgot password?" link #9CA3AF (gray-400) ~2.5:1 4.5:1
Footer "FAMILIENARCHIV" watermark #C5C1B8 (approx) ~1.8:1 decorative — OK if purely decorative, add aria-hidden="true"
"FROM Unknown" / "TO Unknown" in document rows #9CA3AF ~2.5:1 4.5:1
"Not specified" / "No recipients" in metadata panel #9CA3AF italic ~2.5:1 4.5:1
"SENDER" / "RECIPIENTS" uppercase labels in metadata #9CA3AF ~2.5:1 4.5:1

Fix — brand-compliant color replacements

The brand palette does not include a ready-made "body gray". The correct approach is to use #000000 (brand black) at reduced opacity, or to use #615B66 (Granite Gray) which is the lightest brand secondary that still passes contrast.

Use case Recommended color Contrast on white
Interactive secondary links ("Forgot password?") #615B66 Granite Gray 5.9:1 ✓ AA
Non-interactive helper text ("Not specified") #615B66 5.9:1 ✓ AA
Uppercase micro-labels ("SENDER", "FROM") #615B66 5.9:1 ✓ AA
Footer watermark (decorative) Keep as-is, add aria-hidden="true" decorative exemption

In Tailwind terms: replace text-gray-400 with a custom utility text-granite mapped to #615B66, or use text-gray-600 (#4B5563, 7.3:1) as a safe fallback if the brand token is not yet set up.

Dark mode: on dark backgrounds, #9CA3AF (gray-400) gives ~4.6:1 on #1f1f1f — just barely passes. Safe to keep for dark mode specifically, but verify against the actual dark background color used.

## Severity: High — accessibility failure (WCAG SC 1.4.3) Multiple instances of secondary/helper text render in a gray that fails the 4.5:1 contrast ratio required for normal-weight text on a white or near-white background. **Affected elements (light mode):** | Element | Current approx. color | Contrast on white | Required | |---|---|---|---| | "Forgot password?" link | `#9CA3AF` (gray-400) | ~2.5:1 | 4.5:1 | | Footer "FAMILIENARCHIV" watermark | `#C5C1B8` (approx) | ~1.8:1 | decorative — OK if purely decorative, add `aria-hidden="true"` | | "FROM Unknown" / "TO Unknown" in document rows | `#9CA3AF` | ~2.5:1 | 4.5:1 | | "Not specified" / "No recipients" in metadata panel | `#9CA3AF` italic | ~2.5:1 | 4.5:1 | | "SENDER" / "RECIPIENTS" uppercase labels in metadata | `#9CA3AF` | ~2.5:1 | 4.5:1 | --- ## Fix — brand-compliant color replacements The brand palette does not include a ready-made "body gray". The correct approach is to use `#000000` (brand black) at reduced opacity, or to use `#615B66` (Granite Gray) which is the lightest brand secondary that still passes contrast. | Use case | Recommended color | Contrast on white | |---|---|---| | Interactive secondary links ("Forgot password?") | `#615B66` Granite Gray | 5.9:1 ✓ AA | | Non-interactive helper text ("Not specified") | `#615B66` | 5.9:1 ✓ AA | | Uppercase micro-labels ("SENDER", "FROM") | `#615B66` | 5.9:1 ✓ AA | | Footer watermark (decorative) | Keep as-is, add `aria-hidden="true"` | decorative exemption | In Tailwind terms: replace `text-gray-400` with a custom utility `text-granite` mapped to `#615B66`, or use `text-gray-600` (`#4B5563`, 7.3:1) as a safe fallback if the brand token is not yet set up. **Dark mode:** on dark backgrounds, `#9CA3AF` (gray-400) gives ~4.6:1 on `#1f1f1f` — just barely passes. Safe to keep for dark mode specifically, but verify against the actual dark background color used.
marcel added the ui label 2026-03-27 12:28:16 +01:00
Sign in to join this conversation.
No Label ui
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#92