From d2751046cd8b814b817062f6fd027153925e1866 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=7BAI=7Df=20D=2E=20M=C3=BCller?= Date: Tue, 14 Apr 2026 09:44:01 +0200 Subject: [PATCH 1/2] fix: Lighthouse a11y + CLS issues on homepage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses real failures surfaced after PR #430 pointed Lighthouse at the canonical deployed URL instead of the stale fork URL. ## Accessibility (0.92 → target 1.0) ### aria-allowed-role + label-content-name-mismatch Anchor cards used
, which fails two axe rules: -
has implicit role "article"; overriding to "button" is invalid per ARIA in HTML spec - aria-label started with "Open" instead of the visible h3 text, so the accessible name didn't match the visible label Fixed by changing
to
and using aria-labelledby pointing at the h3 id. Accessible name now exactly matches the visible title. Event delegation uses the .anchor-card class, not the tag name, so nothing breaks. ### select-name #header-role-filter had no associated label. Added aria-label referencing the existing "All roles" translation string. ### color-contrast .meta-badge text inherited .anchor-card-meta's gray-500/400 colors, which only reach ~3.9:1 (light) / ~3.1:1 (dark) on the badge's gray-100/700 background — below the WCAG AA 4.5:1 threshold for normal text. Explicitly set gray-700/200 on .meta-badge for ~5.3:1 in both themes. ## Performance (0.75 → target ≥ 0.9) ### cumulative-layout-shift (weight 25) #page-content started empty and populated asynchronously with the card grid, causing the single biggest CLS contributor on the page. Added min-height: calc(100vh - 12rem) to reserve viewport space so the browser doesn't shift layout when the content arrives. ### unsized-images Logo images (header + onboarding modal) had no explicit width/height, so the browser couldn't reserve space and had to reflow when the image loaded. Added width/height attributes derived from the actual logo.png aspect ratio (1024×451) and added w-auto so the CSS height constraint still wins visually. Co-Authored-By: Claude Opus 4.6 (1M context) --- website/src/components/card-grid.js | 8 ++++---- website/src/components/header.js | 9 ++++++--- website/src/components/onboarding-modal.js | 2 +- website/src/styles/main.css | 11 +++++++++++ website/src/translations/de.json | 1 + website/src/translations/en.json | 1 + 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/website/src/components/card-grid.js b/website/src/components/card-grid.js index 0281af48..4e563de2 100644 --- a/website/src/components/card-grid.js +++ b/website/src/components/card-grid.js @@ -112,17 +112,17 @@ function renderAnchorCard(anchor, categoryColor) { const safeId = escapeHtml(anchor.id) return ` -
-

${escapeHtml(anchor.title)}

+

${escapeHtml(anchor.title)}

+
` } diff --git a/website/src/components/header.js b/website/src/components/header.js index 7c04cec9..0bda6f3a 100644 --- a/website/src/components/header.js +++ b/website/src/components/header.js @@ -11,7 +11,7 @@ export function renderHeader() {
- Semantic Anchors + Semantic Anchors ${i18n.t('header.slogan')}