You press Tab on a page and VoiceOver says link, Home, Primary, navigation. Not the content. On a real site that header can hold dozens of links, and keyboard and screen reader users have to tab through it on every page load. Add a skip link and the problem looks solved. But if its href points at an id that doesn't exist, activation goes nowhere.
WCAG 2.4.1 Bypass Blocks (Level A) requires a way to skip blocks of content that repeat across pages. A skip link only counts if it actually moves the user past the repeated block.
Two ways it breaks
-
No skip link. The first focusable node is
link "Home"insidenavigation "Primary". You only reachmainafter every nav link. There's no mechanism at all. - Skip link with a mismatched target. This one looks fixed. The link is first in Tab order and visible on focus, and VoiceOver announces it correctly: visited, link, Skip to main content. But the code looks like this:
<a href="#main">Skip to main content</a>
<header>Study Lab</header>
<nav aria-label="Primary">…</nav>
<main id="skip-to-content-broken-case2-main">…</main>
There's no element with id="main". After activation, focus is still inside the navigation. The problem isn't what the link says. It's where it goes, and you only see that in the accessibility tree, not in the announcement.
Auditors test activation, not presence: press Enter and check where focus goes. Landmarks like nav and main help screen reader users, but sighted keyboard users have no landmark shortcut, so a broken skip link is still a defect.
The fix
Make the skip link the first focusable element, make its href fragment match a real id, and give the target tabindex="-1" so it can take programmatic focus:
<a class="skip-link" href="#main-content">Skip to main content</a>
<header>…</header>
<nav aria-label="Primary">…</nav>
<main id="main-content" tabindex="-1">…</main>
In React/JSX that's tabIndex={-1}. -1 lets main take focus from script or fragment navigation without adding it to the Tab order.
Hide it until it's focused:
.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
left: 1rem;
top: 1rem;
z-index: 1000;
}
Behaviors to verify
- The first Tab on the page reaches the skip link, and it's visible when focused.
- Pressing Enter moves focus into the main content, so the next thing read is content, not navigation.
- The
hreffragment matches an id that exists exactly once, and the target hastabindex="-1". Check it in the accessibility tree and with a screen reader before shipping.
Practice it
- Video: https://youtu.be/uuevKPHlgDk
- Short: https://youtube.com/shorts/gnGrnoXHhCI
- Companion write-up: https://www.wasexamprep.com/blog/skip-link-is-not-a-bypass
Takeaway: a skip link that doesn't land is not a bypass. Test activation, not presence.
If this was useful, subscribe to A11Y in Practice on YouTube (@a11yinpractice) for more hands-on accessibility episodes, and visit wasexamprep.com for WAS study notes and labs.
A11Y in Practice and wasexamprep.com are independent educational resources. They are not affiliated with or endorsed by IAAP (International Association of Accessibility Professionals).
Top comments (0)