AccessibilityTYPENORMLabs7 minJuly 25, 2026

Skip Navigation Links for Accessibility

A skip navigation link lets a keyboard user jump past the nav straight to the content. Four lines of markup, and almost every implementation gets the CSS or the focus wrong.

Open your product and press Tab. Count the stops before focus reaches a sentence of actual copy. Logo, primary nav, every item in every dropdown, the search field, the login button, the CTA. On a busy marketing site that's a lot of keystrokes, and a keyboard user pays them again on the next page, and the page after that.

A skip navigation link is the fix. It's a link, usually the first focusable thing in the document, that jumps focus past all of that and lands it on the main content. Sighted mouse users never see it. Keyboard and screen-reader users hit it first and skip the tour. It's about four lines of code, it's been standard practice for twenty years, and it's still broken more often than it works. The markup isn't the hard part. The CSS that hides it is.

Who a skip navigation link is actually for

The usual mental model is "screen reader users." That's half right.

Screen readers already have shortcuts for this. NVDA, JAWS and VoiceOver all let you jump between landmarks and headings, so an experienced screen-reader user can reach <main> without your help. They still benefit, because not everyone knows the shortcuts and a skip link is faster than hunting through a landmarks list.

The people with no alternative are sighted keyboard users. Someone with a motor impairment using a switch or a head pointer. Someone with RSI who can't hold a mouse. Someone whose trackpad died this morning. A power user who just doesn't reach for the mouse. None of them get a landmark menu. They tab, in order, through everything you put in front of the content, so for them the skip link is the only bypass mechanism on the page.

That's why WCAG treats it as a baseline. Success criterion 2.4.1 Bypass Blocks is Level A. That's the floor. Clear it or you don't get to claim conformance at all. It requires a way to skip content that repeats across pages, and a skip link is the simplest way to satisfy it. This is the same everyday accessibility work as naming your buttons and checking your color contrast. Unglamorous, cheap, immediately felt by the people who need it.

The markup is four lines

<body>
  <a class="skip-link" href="#main">Skip to main content</a>
  <header>…nav…</header>
  <main id="main" tabindex="-1">…</main>
</body>

It has to come first in the DOM. Tab order follows source order, so the link goes before the header or it skips nothing. The tabindex="-1" on the target matters just as much, and it gets its own section below, because it's the failure that hides best.

The text should say where you're going. "Skip to main content" is the phrasing users recognize. "Skip" on its own leaves people guessing what they just agreed to.

That's the whole feature. Everything that goes wrong from here is CSS and focus.

The CSS that quietly kills it

Most teams don't want the link visible at all times, so it gets hidden. The two most obvious ways to hide something both remove it from the tab order entirely:

/* Both of these break the skip link. It can never be focused. */
.skip-link { display: none; }
.skip-link { visibility: hidden; }

A skip navigation link that can't receive focus is worse than none at all, because it passes a casual code review. The markup is right there in the DOM, doing nothing. The same trap catches width: 0; height: 0, and opacity: 0 paired with pointer-events: none.

What you want is a link that's visually hidden but still focusable, and that becomes visible the moment it's focused:

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  padding: 0.75rem 1rem;
  background: #fff;
  color: #000;
  z-index: 100;
}

.skip-link:focus {
  left: 0;
}

Two smaller things bite here. outline: none on a link whose entire purpose is to be seen when focused is self-defeating, so leave the focus ring alone. And if your header is sticky, the target lands underneath it after the jump; scroll-margin-top: 6rem on #main pushes it clear.

The focus bug nobody tests for

This is the one that survives code review. You Tab to the skip link, press Enter, and the page scrolls to the content. It looks perfect. Then you press Tab again and focus goes back to the second item in the nav, because the page scrolled but focus never actually moved.

A fragment link only moves keyboard focus if its target can hold focus. <main>, <div>, <section> and headings can't, natively. Browsers have improved here, and most modern ones now reset the sequential focus starting point when you follow a fragment link. But the behavior was inconsistent across browsers and versions for years, and "most browsers, probably" is a weak foundation for an accessibility feature.

One attribute fixes it:

<main id="main" tabindex="-1">

tabindex="-1" makes an element programmatically focusable without adding it to the tab order, so the skip link can put focus on it and nothing else about the page changes. Add it to whatever your skip target is. You can do the same job in JS on the link's click, calling document.getElementById('main').focus(), but the attribute is simpler and doesn't depend on a script having loaded.

Skip navigation in a single-page app

Client-side routing breaks the assumption the whole pattern rests on.

On a server-rendered page load, focus starts at the top of the document. That's free, and it's what makes the skip link genuinely the first thing you reach. In a React or Vue app there is no new document. The router swaps the DOM while focus stays wherever it was, usually on the nav link that triggered the navigation, sometimes on <body> after the element holding focus gets unmounted. Neither is the top of the new page in any sense the user can act on, and the skip link they'd want is now somewhere behind them in the tab order.

So move focus yourself on every route change. After navigation completes, focus the main content container (the same tabindex="-1" element) or the <h1>. This reproduces what a full page load does for free, and it's what puts the skip link back within reach on the next Tab.

Then check that your router isn't eating the hash. Some treat href="#main" as a route to resolve and swallow the fragment before the browser sees it. If yours does, bind a click handler that calls preventDefault(), focuses the target, and scrolls it into view. Worth testing explicitly rather than assuming, since it fails silently and only for keyboard users.

Announcing the new page title in a live region is a good companion fix, but it's a different problem. That one tells people where they are. The skip link decides where their focus goes.

How many skip links is too many

One is usually right. Two is defensible: "Skip to main content" plus "Skip to search" on a site where search is the primary task.

The sub-case worth catching is a long repeated block inside the page, like a fifty-item filter sidebar on every category page. That's also a bypass problem under 2.4.1, and a local "Skip filters" link is a reasonable answer. But if you're adding a fourth skip link, the real problem is how much repeated, tab-stoppable furniture sits between a user and the thing they came for.

Testing your skip navigation link

This is fast to verify, which is why there's no excuse for shipping it broken. Load the page fresh, then:

  1. Press Tab once. The skip link should appear, visibly, with a focus ring.
  2. Press Enter. The view should jump to the main content.
  3. Press Tab again. Focus must land on the first interactive element inside the content, not back in the nav. This is the step that catches a missing tabindex="-1".
  4. Repeat after a client-side route change, if you have one.
  5. Repeat with a screen reader on, listening for whether the link announces itself sensibly.

Automated checkers help with step 1 and almost nothing after it. Most can confirm a skip link exists. None can tell you focus actually moved, because that's a behavior rather than a property of the DOM.

One thing to check while you're in there with the keyboard. The focus indicator you just relied on has to be visible against its background, which is a contrast question. Our free contrast checker will tell you in a few seconds whether the ring you're seeing on your monitor clears the threshold on someone else's.

FAQ

What is a skip navigation link? A link at the very start of a page that moves focus past repeated content, usually the header and nav, straight to the main content. It's typically hidden until it receives keyboard focus, so mouse users never see it while keyboard users get it as their first tab stop.

Is a skip navigation link required by WCAG? WCAG doesn't require this specific technique. It requires a bypass mechanism under 2.4.1 Bypass Blocks (Level A), and a skip link is the most common way to satisfy it. Proper landmarks and headings can also qualify, but they help screen-reader users far more than sighted keyboard users, which is why most teams ship the link anyway.

Should the skip link always be visible? It doesn't have to be. Visually-hidden-until-focused is the norm. Permanently visible is a legitimate choice too, and plenty of government and university sites do exactly that. It's the version that's hardest to break by accident, since no CSS trick can hide it unintentionally.

Where should a skip navigation link go in the HTML? As the first focusable element in the <body>, before the header. Source order determines tab order, so a link placed after the nav will be reached too late to skip anything, no matter where CSS positions it on screen.

Why doesn't my skip link move focus? Almost always because the target isn't focusable: add tabindex="-1" to the element the link points at. If the link never appears at all, look for display: none or visibility: hidden.

Does a skip navigation link affect SEO? Not meaningfully. It's an ordinary internal anchor and search engines handle it fine.

The block you forgot to skip

Here's the failure mode most likely to hit you after everything above is correct. Your skip link is only first in the tab order if nothing else gets there before it. A cookie banner that traps focus on load, a promo modal, a chat widget that mounts late and grabs focus, an ad iframe injected above the header — any of these puts something in front of the bypass mechanism whose whole job was to be in front.

So when you run the five-step test, run it on a fresh session with consent not yet given, on the page where the widget actually fires. That's the state a first-time visitor arrives in, and it's the one nobody QAs.

Free UX Snapshot for 50 Product Teams

Apply now and get a complimentary UX Snapshot — our rapid clarity audit delivered in 48 hours. Limited to the first 50 products.

Apply for Free UX Snapshot

Related

Information Architecture

UX Collective UX Teardown: Building the Biggest Design Publication on Rented Land

A UX teardown of UX Collective: the largest independent design publication runs entirely on Medium, and every page is shaped by owning the taste but not the platform. How the read flow races Medium for the reader — and the one tab that wins.

TYPENORMLabs · 5 min · July 24, 2026

Web
Information Architecture
Interaction Design

Research Methods

Usability Testing — A Step-by-Step Guide

How to run a usability test end to end: framing the question, picking moderated or unmoderated, recruiting five people, writing tasks that don't lead, and turning what you watched into a ranked list of fixes.

TYPENORMLabs · 8 min · July 29, 2026

Research Methods
UX Clarity
Web

Information Architecture

Nielsen Norman Group UX Teardown: The Oldest Funnel in UX, Run in Plain Sight

A UX teardown of Nielsen Norman Group's site: how the firm that made usability a profession turns a free article library into five- and six-figure consulting — three tiers of the same authority, priced for three different buyers.

TYPENORMLabs · 5 min · July 24, 2026

Web
Information Architecture