From 217dd88f2553fedc026a9934e5555201cee05fda Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 9 May 2026 00:01:17 -0400 Subject: [PATCH] A11y pass: WCAG 2.2 AA across the dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tokens (single CSS edit clears most contrast failures) - Remap --color-text-muted from text-tertiary (#9CA3AF, 2.54:1) to gray-600 (#4B5563, 8.59:1). Eyebrow, stat labels, column headers, intervals, pending notes, etc. now all clear 4.5:1 at the small sizes we render them. - Introduce --color-warning-text (#d9480f), --color-danger-text (#b91c1c), --color-success-text (primary-700). Badge variants now use text-warning-text / text-danger-text / text-success-text / text-primary-strong on -soft fills, all >=4.5:1. - Active country pill, sensitivity pill, and active nav switch from bg-primary text-void / text-primary (~3.3-3.5:1) to bg-primary-strong text-white / text-primary-strong (>=7:1). Document landmarks - Each page now has an

(sr-only on / and /paper so the existing visual hierarchy survives). -
on both pages plus a "Skip to main content" link in layout.tsx (visually hidden until focused). - Per-route metadata so /paper has a distinct browser tab title. Focus and keyboard - Global :focus-visible ring on every interactive element via globals.css, scoped to focus-visible so mouse clicks don't show it. - Compact ViewSelector pills bumped to 24x24+ (WCAG 2.2 SC 2.5.8). - Disabled sensitivity buttons use aria-disabled + click guard instead of the HTML disabled attr, so screen readers can read the title / context explaining why ("no UK rows under this slice; ..."). Active state strips aria-pressed when disabled. - Hidden header chrome stays out of tab order (already correct); re-enables once visible. Live regions and table semantics - Leaderboard wrapped in role="region" aria-live="polite" with a status label that announces model count and active view. The grid uses role="row" / role="columnheader" / row-level aria-label so SR users hear "Rank N, model name, score X%". - Bootstrap interval text now carries aria-label ("Rank 1 across bootstrap draws; 95% score interval ...") so it reads as a sentence, not opaque numbers. - Open-set caveat is an