How to Add a Skip to Content Link That Actually Works
Press Tab on a typical news site and count how many times you hit a key before you reach the first headline. On many pages it is 30 to 60 stops: logo, search, every menu item, sign in, cookie settings.

Press Tab on a typical news site and count how many times you hit a key before you reach the first headline. On many pages it is 30 to 60 stops: logo, search, every menu item, sign in, cookie settings. A skip to content link turns that into one keypress, and it is one of the cheapest accessibility fixes you can ship.
It is also one of the most commonly broken. Plenty of sites have the link in their markup, yet it jumps nowhere, never becomes visible, or moves the scroll position while focus stays stuck in the header. Here is how to build one that works in every major browser and with screen readers.
Who Uses a Skip Link and Why It Matters
Skip links help anyone who navigates with a keyboard instead of a mouse. That includes screen reader users, people with motor impairments who use switch devices or sip and puff controls, and power users who simply prefer the keyboard.
WCAG 2.2 success criterion 2.4.1, Bypass Blocks, requires a way to skip content repeated on multiple pages. A skip link is the most direct way to meet it. Landmarks such as a proper main element help screen reader users too, but sighted keyboard users cannot jump between landmarks without extra tools, so the link still matters.
The Minimal Markup for a Skip to Content Link
The link should be the very first focusable element inside the body, before the logo and navigation. It points to the id of your main content region.
<body>
<a class="skip-link" href="#main">Skip to main content</a>
<header>...</header>
<main id="main" tabindex="-1">
<h1>Page title</h1>
</main>
</body>
Three details make this robust:
- A real anchor with an href. A button or a div with a click handler will not behave the same way for keyboard and assistive technology.
- A target with a matching id. Check the id exists on every page template, not only the homepage.
- tabindex="-1" on the target. This lets the main element receive focus programmatically without adding it to the normal tab order.
Why tabindex on the Target Still Matters
Modern browsers move the sequential focus starting point to a fragment target, even when that target is not focusable. That means the next Tab press usually lands inside the main content. So why bother with tabindex?
Two reasons. First, some screen reader and browser combinations announce the new position more reliably when the target actually receives focus. Second, if your site is a single page app that intercepts link clicks for routing, the default fragment behaviour may never run. Adding tabindex="-1" and focusing the element yourself makes the result predictable.
One side effect: some browsers draw a focus outline around the whole main element. You can suppress that specific outline with a rule on main:focus, but keep visible focus styles everywhere else. Our article on why focus outlines keep getting removed from buttons and links covers the wider problem.
Hiding the Link Until It Receives Focus
Most designs hide the skip link until a keyboard user tabs to it. The hiding method is where many implementations go wrong.
.skip-link {
position: absolute;
left: 1rem;
top: -100px;
padding: 0.75rem 1rem;
background: #111;
color: #fff;
z-index: 1000;
}
.skip-link:focus {
top: 1rem;
}
Avoid these approaches:
- display: none or visibility: hidden. The link is removed from the tab order entirely, so nobody can use it.
- Clipping that never resets on focus. A visually hidden utility class without a focus override leaves keyboard users activating an invisible control.
- Low contrast styling. When it appears, the link needs at least a 4.5 to 1 contrast ratio against its background, like any other text.
If your design allows it, consider keeping the link permanently visible in a small top bar. It removes a whole class of bugs and helps users who do not know skip links exist.
Making It Work in Single Page Apps
Client side routers often break skip links in two ways. They may treat the hash as a route and navigate away, or they may re-render the page so focus lands on the body after every route change.
A small handler solves both:
document.querySelector('.skip-link').addEventListener('click', (e) => {
const target = document.getElementById('main');
if (!target) return;
e.preventDefault();
target.focus();
target.scrollIntoView();
});
Pair this with focus management on route changes. After navigating, move focus to the new page heading or main region so screen reader users hear that the content changed. Test both behaviours together, because fixing one can quietly break the other.
Choosing the Wording and Placement
The link text should say exactly where it goes. "Skip to main content" is the most widely recognised wording, and screen reader users who have met it on hundreds of sites know what it does instantly. Clever labels such as "Jump in" or an icon alone save a few pixels and cost clarity.
Placement matters just as much. If a cookie banner, promo bar or language selector sits above the link in the DOM, keyboard users must tab through those first, which defeats the purpose. Move the skip link above them in the source order, even if the banner is visually on top. When you translate the site, translate the link text too; a lone English label on a German page is easy to miss in reviews.
Finally, check that the target is the start of the unique content, usually just before the h1. Pointing it at a breadcrumb trail or a secondary filter bar only moves the problem a few stops down the page.
Testing Your Skip Link in Five Minutes
Run this quick test on every page template, not only the homepage:
- Load the page fresh, click in the address bar, then press Tab once. The skip link should appear and be clearly visible.
- Press Enter. The page should scroll to the main content.
- Press Tab again. Focus should land on the first link or control inside main, not back in the header.
- Repeat with a screen reader such as NVDA on Windows or VoiceOver on macOS, and listen for the announcement of the main region or heading.
- Repeat in Safari, where you may need to enable Tab key navigation for links in the browser settings first.
For a fuller checklist that covers menus, modals and forms, follow our keyboard navigation accessibility test.
FAQ
Is a skip link required for WCAG compliance?
Not strictly. WCAG 2.4.1 requires a way to bypass repeated blocks, and proper landmarks or headings can satisfy it, but a skip link is the most reliable way to help sighted keyboard users.
Should a page have more than one skip link?
Usually one is enough. Complex layouts with long sidebars or search tools can add a second, such as skip to search, but more than two or three starts recreating the problem you set out to fix.
Why does my skip link scroll but not move focus?
The target is probably being re-rendered or intercepted by a router. Add tabindex="-1" to the target and call focus on it in a click handler so the browser focuses it explicitly.
Small Link, Big Difference
A working skip to content link needs four things: it comes first in the DOM, it targets a real id, it becomes visible on focus, and it moves focus, not just the scroll position. Test it on every template and in at least two browsers. For more practical fixes, browse our accessibility guides.
More in Accessibility
Accessibility
How to Write Alt Text That Helps Screen Reader Users
A screen reader user lands on your product page and hears "image, IMG_4471.jpg" three times in a row.
Accessibility
Color Contrast Ratios Explained for Readable Web Text
The grey #777777 on a white background has a contrast ratio of 4.48 to 1.
Accessibility
Accessible Form Error Messages Screen Readers Announce
A user fills in a 12 field sign up form, presses submit, and the page reloads with two fields outlined in red.