Skip to content

fix(a11y): tabindex on <main> so the skip link moves focus reliably (WCAG 2.4.1)#3451

Open
bilal-karim wants to merge 1 commit into
mainfrom
a11y/2.4.1-skip-link-target-focus
Open

fix(a11y): tabindex on <main> so the skip link moves focus reliably (WCAG 2.4.1)#3451
bilal-karim wants to merge 1 commit into
mainfrom
a11y/2.4.1-skip-link-target-focus

Conversation

@bilal-karim
Copy link
Copy Markdown
Member

@bilal-karim bilal-karim commented May 25, 2026

Summary

One-attribute change: add tabindex=\"-1\" to <main id=\"content\"> in main-content-container.svelte so the skip-navigation link reliably moves focus across browsers.

- <main id="content" class="pb-16 md:pb-0">
+ <main id="content" tabindex="-1" class="pb-16 md:pb-0">

The defect

SkipNavigation (src/lib/components/skip-nav.svelte) renders <a href=\"#content\">. Activating it should both visually scroll the target into view AND programmatically move focus to it. By HTML spec, a <main> without tabindex is not focusable — so:

  • Chrome / Edge: focus moves to the target (Chromium has an in-page-anchor workaround). ✓
  • Safari / older Firefox: page scrolls, but focus stays on the skip link itself. ✗

In affected browsers, the skip link looks like it works (page scrolls) but the user's next Tab cycles right back through the nav chrome — defeating the bypass for keyboard and screen-reader users.

The fix

tabindex=\"-1\":

  • Makes <main> programmatically focusable so the hash-link navigation lands focus there
  • Keeps it out of the natural Tab order (negative tabindex)
  • Cross-browser-consistent

Closes WCAG 2.4.1 sufficient-technique G1's "programmatically moves focus" requirement.

Audit context

  • WCAG 2.2 SC 2.4.1 Bypass Blocks (Level A) — Medium remediation priority.
  • Issue file: audit-output/issues/2.4.1-skip-link-target-focus.md.
  • Currently partially mitigated (works on Chromium-majority browsers); this PR closes the cross-browser gap.

Test plan

  • Safari: load any app page, press Tab once (reveals skip link), press Enter — confirm focus moves into the main content area (next Tab should now navigate within the content, not back to the nav).
  • Chrome / Edge: same flow — confirm unchanged behavior (still works).
  • Firefox: same flow — confirm focus moves on activation.
  • Zero visual change on any page.

Functional impact

Pure attribute addition. tabindex=\"-1\" does not place <main> in the natural Tab order, so:

  • Normal keyboard navigation through page content is unaffected
  • Skip link's existing scroll behavior is unaffected
  • Only programmatic focus targeting (<a href=\"#content\">, focus(), etc.) gains a target

No risk to existing behavior.

🤖 Generated with Claude Code

A11y-Audit-Ref: 2.4.1-skip-link-target-focus

…WCAG 2.4.1)

The SkipNavigation primitive renders <a href="#content">, which
targets <main id="content"> in main-content-container.svelte. By
HTML spec, <main> without tabindex is not programmatically
focusable -- so activating the skip link only reliably moves
focus on Chromium-based browsers (Chrome, Edge). Safari and
older Firefox scroll the target into view but leave focus on the
skip link itself, defeating the bypass for keyboard and screen-
reader users.

Adding tabindex="-1" makes <main> programmatically focusable
(so the hash-link navigation lands focus there) while keeping it
out of the natural Tab order. Closes WCAG 2.4.1 sufficient-
technique G1's "programmatically moves focus" requirement across
all browsers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bilal-karim bilal-karim requested a review from a team as a code owner May 25, 2026 20:13
@vercel
Copy link
Copy Markdown

vercel Bot commented May 25, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
holocene Ready Ready Preview, Comment May 25, 2026 8:14pm

Request Review

@github-actions github-actions Bot added a11y Accessibility audit PR a11y:no-fix-doc No A11y-Audit-Ref line; audit team triage a11y:bucket-2 Bucket 2: design-mergeable, HTML / ARIA a11y:sc-2.4.1 and removed a11y:no-fix-doc No A11y-Audit-Ref line; audit team triage labels May 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y:bucket-2 Bucket 2: design-mergeable, HTML / ARIA a11y:sc-2.4.1 a11y Accessibility audit PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant