Skip links
One link, hidden until focused, that saves keyboard users dozens of tab presses on every page.
Open any news site. Count the links in the top navigation: site logo, search, sign in, every category, every subcategory. Twenty links is normal. Now imagine pressing Tab twenty times every single page, just to reach the article. That's the keyboard-user experience without a skip link.
A skip link is the first link on the page. Press Tab once, and a button appears (usually top-left) that says something like "Skip to main content". Activate it, and focus jumps past the navigation. One key. Done.
It is the single highest-leverage accessibility feature on the web, and it takes maybe ten lines of CSS plus a single anchor tag. There is no excuse.
Why skip links exist
Skip links exist because keyboard navigation is sequential. A mouse user can click the article body directly. A screen-reader user has rotor menus and headings to jump around with. But a sighted keyboard user — someone with a hand injury, an RSI, a tremor, or just a strong preference — has only Tab and Shift+Tab.
Without a way to skip, every page begins with the same procession of header links. Multiply that by every navigation event in a session, and you've made the site ten times slower for them than for a mouse user.
WCAG calls this success criterion 2.4.1, "Bypass Blocks." Most landmark elements (<nav>, <main>, <aside>) help screen readers, but a skip link is the only mechanism that helps a sighted keyboard user too.
If you remember nothing else from this course: add a skip link. Right now. To everything you ship.
Building one
The recipe has two parts: the link, and the target.
<body> <a href="#main" class="skip-link">Skip to main content</a> <header> <nav>...lots of links...</nav> </header> <main id="main" tabindex="-1"> <h1>Article title</h1> <p>...</p> </main> </body>
A few things to notice:
- The skip link is the very first focusable element in the body. It must come before the navigation, or it can't skip past it.
- The target is
<main>with anid="main". The browser scrolls there and moves focus to it. tabindex="-1"on<main>is the trick that makes focus actually move. By default, focus only follows fragment links to elements that are already focusable (links, buttons, inputs).<main>is not focusable on its own. Withtabindex="-1", it becomes programmatically focusable, so the skip works in every browser.
Without tabindex="-1", several browsers will scroll to the target but leave focus on the link — so the next Tab press goes back into the navigation you were trying to skip.
The visually-hidden trick
The skip link should be invisible until focused — visible to keyboard users, out of the way for everyone else.
.skip-link {
position: absolute;
top: 0;
left: 0;
padding: 0.75rem 1rem;
background: #000;
color: #fff;
/* Move it offscreen, but stay focusable */
transform: translateY(-120%);
transition: transform 0.15s;
z-index: 100;
}
.skip-link:focus {
transform: translateY(0);
}Three rules to live by:
- Don't use
display: none. That removes it from the tab order entirely, defeating the point. - Don't use
visibility: hidden. Same problem. - Do keep it focusable but visually offscreen, then bring it back with
:focusor:focus-visible.
A common older pattern uses clip: rect(0 0 0 0) or width: 1px; height: 1px; overflow: hidden as a "visually hidden" technique. Those work, but they don't animate, and the link sometimes flashes a tiny dot when focused. The transform approach above is cleaner.
More than one skip link?
For most sites, one skip link is plenty. Long, multi-section pages (a documentation site, a long form, a multi-step checkout) might benefit from two: "Skip to main content" and "Skip to footer," or "Skip to filters" and "Skip to results."
If you add multiple, keep them stacked together at the very top, and order them by usefulness (most-skipped-to first).
tabindex="-1"?