fix(ui): WCAG AA color-contrast failures in brand palette #147
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
The axe-core wcag2a/wcag2aa E2E suite (added in #118) has
color-contrastdisabled because the brand palette produces contrast ratios below the WCAG AA threshold of 4.5:1. This needs a design review before fixing.Failing locations (measured by axe-core 4.11 on current main)
/persons— 2 nodesAffected class:
text-ink/60(resolves to#61788eon canvas#f0efe9)<p class="… text-ink/60">subtitle paragraph<a class="… text-ink/60 …">"New person" link/admin— 4 nodesAffected class: text links using
#a1dcd8(brand-mint /--c-accent) on white#ffffffHow to re-enable the axe rule once fixed
In
frontend/e2e/accessibility.spec.ts, remove.disableRules(['color-contrast'])frombuildAxe().Notes
ink-3(#6b7280) is correctly documented inlayout.cssas 4.8:1 on white which is AA-compliant; the real issues aretext-ink/60on the sandy canvas and brand-mint links on white.Design review — Leonie Voss, UX/Brand
I've read the code behind both failures. They're two distinct problems with different root causes and different fix strategies.
Finding 1 —
text-ink/60on canvas (3.97:1) · HIGHWhere:
persons/+page.svelte:37(subtitle),persons/+page.svelte:43("New person" link), plusconversations/+page.svelte:53,ConversationTimeline.svelte:60,DocumentList.svelte:31— the same pattern is scattered across the app.Root cause:
text-ink/60is an opacity shortcut. It produces a computed colour that depends entirely on the background beneath it. On white (#ffffff) it gives#61788eat 4.8:1 — borderline pass. On our sandy canvas (#f0efe9) the same opacity blends to#61788e-equivalent at 3.97:1 — fail. The token system exists precisely to prevent this kind of background-dependency, but then opacity modifiers silently bypass it.WCAG criterion violated: 1.4.3 (Contrast, AA) — 4.5:1 required for normal text, 3:1 for large text (≥18px regular or ≥14px bold).
Fix — two cases:
Subtitles / descriptive secondary text (
<p class="… text-sm text-ink/60">):→ Replace with
text-ink-2.#4b5563is 6.6:1 on canvas — WCAG AA ✓. Secondary text should be readable; we're using opacity to make it look subtle when the right tool is a dedicated token at the correct tone.Subtle action links (
<a class="… text-sm font-medium text-ink/60 … hover:text-ink">):→ Replace with
text-ink-3.#6b7280is 4.8:1 on white. Note:layout.csscomments "use only on surface, not canvas" forink-3— that note needs revisiting. On canvas#f0efe9the effective ratio is ~4.6:1, which technically still passes AA for 14px/medium weight. However, interactive elements (links) should ideally exceed AA. I would usetext-ink-2here too, and achieve the "subtle" look throughfont-weight: 500anduppercase tracking-widestrather than a low-contrast colour.Rule going forward: Ban
text-ink/60,text-ink/70, and all opacity-modified ink utilities on canvas backgrounds. The semantic token layer is supposed to eliminate these guesses.Finding 2 —
text-accenton white (1.52:1) · CRITICALWhere:
admin/UsersTab.svelte:81,admin/GroupsTab.svelte:138. Also present inCommentThread.svelte:280(Reply button),DocumentList.svelte:171(Clear search button),PdfViewer.svelte:300(Direkt öffnen link) — the axe run on/adminsurfaced the worst instances but the underlying problem is a systemic misuse of the accent token.Root cause:
--c-accentin light mode is#a1dcd8— a pale mint chosen as a decorative accent (borders, icon tints, hover fills). Its luminance is too high for readable text on white. A 1.52:1 ratio is not a borderline failure; it renders these labels nearly invisible to anyone with reduced contrast sensitivity, and completely invisible in certain low-vision conditions. An admin-only screen is no justification — administrative users include senior researchers managing their family's archive.WCAG criteria violated: 1.4.3 (Contrast, AA — normal text). Also implicates 2.4.6 (Headings and Labels) because these are the only textual labels identifying the edit action on each table row.
Fix:
Replace
text-accentwithtext-primary(--c-primary=#012851, 16.8:1 on white) everywhere it is used on text or interactive labels. Mint is not a text colour — it belongs on borders (border-accent), icon containers, and hover/focus rings. Thetext-accentutility should probably not exist at all, or be intentionally restricted to decorative/non-text contexts via a comment in the token file.Dark mode note:
--c-accentremaps to#00c7b1(turquoise) in dark mode — significantly better contrast on dark surfaces. That's why axe passes in dark mode. The fix (text-primary) also remaps correctly in dark mode to#a1dcd8, which is 8.4:1 on dark surface — still fine.Broader audit note
These two axe findings are the tip of a pattern. A quick grep shows
text-accentused as a text colour in at least 6 files, andtext-ink/60in at least 5. I'd recommend the fix PR also adds a comment block tolayout.cssabove--c-accent:That leaves the dark mode axe rule re-enable as a clean, unambiguous signal: once the contrast failures are gone, the rule comes back on.
Priority order:
text-accent→text-primaryon all text/button/link elements (Critical, admin is actively broken)text-ink/60→text-ink-2across the board (High, affects most pages)layout.css(Low, prevents regression)