Skip Links
Add skip links so keyboard users bypass repetitive nav — placement, focus styles, target main, and SPA considerations.
- accessibility
- skip-links
Keyboard and screen reader users should not have to Tab through a mega-nav on every page. A skip link is the first focusable control: “Skip to main content.” It jumps focus to the primary content landmark.
Docs: WCAG 2.4.1 Bypass Blocks, WebAIM skip navigation.
Minimal implementation
<body>
<a class="skip-link" href="#main">Skip to main content</a>
<header>…nav…</header>
<main id="main" tabindex="-1">
<h1>Dashboard</h1>
…
</main>
</body>
tabindex="-1" lets the main target receive programmatic focus without adding an extra Tab stop permanently.
Visually hidden until focused
.skip-link {
position: absolute;
left: 0.5rem;
top: 0.5rem;
transform: translateY(-150%);
z-index: 10000;
padding: 0.5rem 1rem;
background: #000;
color: #fff;
}
.skip-link:focus {
transform: none;
outline: 2px solid #fff;
outline-offset: 2px;
}
Don’t display: none in a way that removes it from focus order. Off-screen / clip patterns that remain focusable are standard.
Multiple skips
Large apps may offer:
- Skip to main
- Skip to search
- Skip to footer
Keep the list short. Label clearly.
<a class="skip-link" href="#main">Skip to main content</a>
<a class="skip-link" href="#search">Skip to search</a>
SPA routing
After client navigation, ensure:
id="main"still exists.- Optionally move focus to main on route change (in addition to skip).
- Title updates — language and page titles.
// on route change
const main = document.getElementById('main');
main?.focus();
Common failures
href="#main"but no matching id.- Target not focusable — focus stays on skip link visually confusing.
- Skip link not first in DOM/tab order.
- Invisible focus styles on the link.
- Smooth scroll only without moving focus.
Interview out-loud answer
“Skip links are the first focusable element, jump to main with href and a focusable target, and become visible on focus. They satisfy bypass blocks for repetitive nav. SPAs must keep the target and manage focus on route changes.”
Where skip links sit in WCAG
2.4.1 Bypass Blocks is the success criterion. Skip links are the classic technique; consistent navigation and landmarks also help. Landmarks alone are not enough for keyboard users who Tab linearly — they still walk the whole header unless a skip exists or they use SR shortcuts.
Multiple landmarks, one skip
If the page has nav, search, and side filters, a single skip to main is usually enough. Adding five skip links creates its own navigation tax. Prefer one primary skip to main, solid landmarks with labels, and a second skip only for huge secondary chrome.
CSS that keeps the link focusable
.skip-link:not(:focus):not(:focus-visible) {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
Verify Tab still lands on it first after styling.
Testing skip links
- Load page, press Tab once — skip link focused and visible.
- Activate (Enter) — focus moves to
#main, not just scroll. - Next Tab enters main content controls.
- Repeat after client-side route changes.
Production checklist
Confirm first-in-tab-order, visible on focus, target id exists, target can take focus, and SPA navigations still work.
Related on this site
- Focus management
- Focus visible styles
- Keyboard accessibility checklist
- Heading hierarchy for a11y
- WCAG principles POUR
Further reading
Related guides
- Accessible Combobox PatternBuild or evaluate comboboxes with APG keyboard behavior, aria-expanded/activedescendant, and filterable listbox pairing.
- Accessible Forms ErrorsLabel inputs, associate errors with fields, announce failures, and avoid placeholder-only forms that break accessibility.
- Accessible Menus PatternAPG menu and menubar keyboard model — arrow navigation, escape to close, aria-expanded, and when a disclosure is enough.
- Accessible Modals PatternsModal dialogs that work: focus trap, Escape, return focus, aria-modal, and why native dialog or APG patterns beat div soup.
- Accessible Names ComputationHow browsers compute accessible names from content, labels, aria-label, and labelledby — and how that powers Testing Library queries.