Add a skip link that lands on the main region
Canonical URL: https://devexamples.com/html/add-a-skip-link-for-keyboard-users/
The problem
On a page with dozens of header links, a keyboard reader must tab through all of them to reach the content, and a hidden skip link that never appears on focus does not help anyone reach it.
Short answer
Make the link the first focusable element, point it at the main region's id, reveal it on focus instead of hiding it off-limits, and give the target a tabindex of -1.
A skip link is three pieces that have to be right together: position, target, and reveal.
<body>
<a class="skip-link" href="#main">Skip to main content</a>
<header>
<nav aria-label="Primary">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/search/">Search</a></li>
</ul>
</nav>
</header>
<main id="main" tabindex="-1">
<h1>Practical developer examples</h1>
</main>
</body>.skip-link {
position: absolute;
inset-inline-start: 0.75rem;
inset-block-start: -4rem;
padding: 0.75rem 1rem;
background: #ffffff;
color: #1b1c20;
border: 1px solid #454953;
border-radius: 0.5rem;
z-index: 5;
}
/* Reveal on focus only. Never display:none, which removes it from the tab order. */
.skip-link:focus-visible {
inset-block-start: 0.75rem;
outline: 3px solid #b35c00;
outline-offset: 2px;
}
main:focus,
main:focus-visible {
outline: none;
}Explanation
The link must be the first focusable element in the document, because its whole purpose is to be reachable before the things it skips. Placing it after the header, or inside the header as the last item, means a keyboard reader meets the repeated navigation first and the mechanism arrives too late to help.
Off-screen positioning rather than display: none or visibility: hidden is the second
requirement: both of those remove the element from the focus sequence entirely, so the shortcut
exists in the markup but nobody can ever reach it. Moving the link into view on
:focus-visible shows it only to the person who tabbed to it, which is why the visual design can
be plain without bothering pointer users.
The tabindex="-1" on the target is the piece most often missed. Fragment navigation moves the
document scroll position, but in some browsers a non-focusable container does not receive focus, so
the next Tab press resumes from the top of the page and walks the header again — exactly the
behaviour the link was supposed to avoid. tabindex="-1" makes it a programmatic focus target
without adding it to the tab sequence, and the accompanying outline: none is acceptable here
because the reader has just performed an explicit action to get there and the heading is now at the
top of the viewport.
Assumptions
- The page has one
mainelement with a unique id, as described in the landmark example. - Text contrast between the link and its background meets 4.5:1 in both themes; the values above are the light-scheme pair.
Common mistakes
- Hiding the link with
display: noneso it can never be focused, which turns the bypass into decoration. - Targeting a wrapper div that is not the content region, so the scroll lands in the wrong place and screen-reader position does not move.
- Adding a skip link only for keyboard users of the header and leaving a long sidebar navigation before the content, which the link does not bypass.
Caveats
- A skip link bypasses blocks of repeated content; it does not substitute for a heading structure, and WCAG expects both for deeply structured pages.
- If your app re-renders the main region on route change, the focus target may be replaced and the browser can lose the position; restore focus deliberately in that case.
Follow-up
Follow-up
Mark up a primary navigation as a labelled listThen make the region you skipped past properly navigable.
Related examples
Editorial links first, then deterministic same-task or same-topic candidates. Tags and shared language alone never qualify a candidate.
Related
Style a visible keyboard focus ring with custom propertiesThe focus ring is what makes the skip link discoverable at all.
Same task or topic
Mark up a primary navigation as a labelled listSolves the Structure page content task
Same task or topic
Structure a page with semantic landmarksSolves the Structure page content task
References
- Understanding WCAG 2.2 — Bypass Blocks(opens in a new tab) — W3C WAI. Defines the mechanism required to bypass repeated content blocks.
- id attribute(opens in a new tab) — MDN. Documents fragment targets used by in-page links.
Source page: https://devexamples.com/html/add-a-skip-link-for-keyboard-users/