fix: semantic card & accessibility cleanup v1 - #20
Merged
Conversation
The 10 works.html and 4 games.html cards used role="link" + tabindex="0" on an <article> that already contained real action links. That is an invalid ARIA pattern (nested interactive content inside a link role), produced a redundant tab stop per card, and made Enter/Space ambiguous. Each card title is now a real anchor to the card's primary destination, and the article is a plain container again. The aria-label that duplicated the heading is dropped now that the heading itself is the link. Titles are far better link text than the generic "View Details". Whole-card mouse navigation is preserved but is now mouse-only, and the guard no longer hijacks modifier clicks, middle clicks or text selection. The fake Enter/Space handlers are removed with the tabindex they served. CSS keeps the heading design identical (the new anchor would otherwise inherit .project-content action-link styling) and moves the pointer affordance from [role="link"] to the data attributes the script reads. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
50 containers used <div aria-label="...">. A plain <div> maps to the generic role, which prohibits an accessible name, so browsers and assistive technology silently dropped every one of those labels. Each of these is a labelled set of related controls, links or items, so they now carry role="group", which supports naming. This is the smallest correct fix: one attribute, no structural or CSS change, and consistent with the role="group" already used by the V2 recruiter role switch and Evidence Explorer toolbar. The three remaining aria-label warnings are on <canvas> elements, where aria-label is the correct way to provide an accessible name. They are reported only as "not recommended" and are kept deliberately. HTML validation: 67 warnings -> 3 (0 errors throughout). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Locks in the semantics this branch fixed so they cannot silently regress: - project cards must not simulate a link (role="link") or carry tabindex - a card with a whole-card navigation target must expose that exact destination as a real title anchor - anchors must never nest - a labelled <div> must carry a role that supports an accessible name - card navigation must stay mouse-only: setupProjectCardNavigation and setupGameCards must route through shouldIgnoreCardActivation and must not re-add simulated keydown activation Each guard was verified to fail when its regression is reintroduced, then restored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
UAJOP
marked this pull request as ready for review
August 22, 2026 13:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Removes invalid interactive-card semantics, fixes accessible names that were being silently dropped, and adds regression guards — while keeping the visual design and mouse UX identical.
The problem
14 cards (10
works.html, 4games.html) were<article role="link" tabindex="0">containing their own links. That is invalid ARIA (interactive content inside a link role), created a redundant tab stop per card, and made Enter/Space ambiguous against the nested anchors.Separately, 50 containers used
<div aria-label="...">. A plain<div>maps to the generic role, which prohibits an accessible name — so every one of those labels was silently discarded by browsers and AT.Changes
Cards → native semantics. Every card already contained a real anchor to its own primary destination, so the card title became that anchor and the
<article>went back to being a plain container. Thearia-label="Open X"is dropped now that the heading is the link — and a title like "SINAMA — AI Agent Reliability Lab" is far better link text than the generic "View Details" it sat next to.Whole-card mouse navigation preserved, but now mouse-only and better behaved: a shared
shouldIgnoreCardActivation()guard skips nested interactive elements, and no longer hijacks modifier clicks, middle clicks or text selection. The simulated Enter/Space handlers were removed along with thetabindexthey existed to serve.Labelled containers now carry
role="group", which supports naming — one attribute, no structural or CSS change, consistent with therole="group"already used by the V2 recruiter role switch and Evidence Explorer toolbar.CSS keeps the design pixel-identical: the new title anchor would otherwise inherit
.project-contentaction-link styling (brand colour,inline-flex,margin-top: 18px), so it explicitly inherits the heading design instead. The pointer affordance moved from[role="link"]to the data attributes the script actually reads.Results
prefer-native-elementaria-label-misuserole/tabindexThe 3 remaining warnings are
<canvas aria-label>— reported only as "not recommended", and the correct way to name a canvas. Removing them would reduce accessibility, so they are kept deliberately.Tab-stop count per card is unchanged (the article stop became the title stop); the improvement is that every stop is now a valid, uniquely-named native link instead of a simulated one wrapping other links.
Verification
qa:portfoliopass ·qa:html0 errors / 3 warnings ·qa:spelling0 ·pa11y-ci11/11 · JS syntax 14/14 · links 0 broken (453 refs; all 14 card title anchors resolve) · Lighthouse a11y/best-practices/SEO 100, CLS ~0 (performance unchanged at the pre-existing CDN-bound 63-66).Browser-verified: whole-card click navigates; nested link wins over the card (proved on the AI Flow Puzzle card, whose "Play" target differs from the card target); modifier/middle clicks and text selection no longer trigger navigation; external links keep
target="_blank"+rel="noopener"; filters, search, Recruiter Mode, role deep links, Command Palette, Ajoop, mobile nav, language and theme all unaffected; 19/19 pages load with no JS errors; 45 viewport×page checks clean at 1440/1366/768/390/360/844×390.8 new regression guards, each verified to fail when its regression is reintroduced, then restored.
No merge. Draft for review.
🤖 Generated with Claude Code