A skip link is a hidden-until-focused link, placed at the very top of every page, that lets keyboard users jump past repeated content (header, primary nav, banners) and land directly on the main content area. It is the primary way to satisfy WCAG 2.2 SC 2.4.1 Bypass Blocks (Level A), and it's usually the first interactive element a keyboard user reaches when they press Tab.
<body> is <a href="#main-content">Skip to main content</a>, with <main id="main-content" tabindex="-1"> as the target.clip-path or position: absolute, revealed when focused — never display: none.Think of a skip link like the express lane at an airport. Most travellers walk the long path through every shop and gate sign. The express lane (the skip link) is invisible to those who don't need it but a huge shortcut for the people who do.
On a typical site, the header and primary navigation contain 20–40 focusable elements. Without a skip link, a keyboard user has to Tab through every one of them — on every page — before reaching the content they came for.
The mechanism is simple: an in-page link whose target is the start of main content. The trick is keeping it visually hidden until focused, and making sure focus actually moves to the target.
<!-- HTML: the skip link must be the first focusable
element inside <body>. -->
<body>
<a class="skip-link" href="#main-content">
Skip to main content
</a>
<header> ... site header and nav ... </header>
<!-- tabindex="-1" makes <main> focusable in every
browser when the skip link is activated. -->
<main id="main-content" tabindex="-1">
... page content ...
</main>
</body>/* CSS: visually hidden until focused.
Use clip-path (or off-screen positioning),
never display: none — display: none removes the
link from the focus order entirely. */
.skip-link {
position: absolute;
top: 0;
left: 0;
padding: 0.75rem 1rem;
background: #000;
color: #fff;
font-weight: 600;
text-decoration: none;
/* Visually hide it without removing it from
the accessibility tree or focus order. */
clip-path: inset(50%);
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
overflow: hidden;
white-space: nowrap;
}
/* When the keyboard user reaches it, reveal it. */
.skip-link:focus,
.skip-link:focus-visible {
clip-path: none;
clip: auto;
height: auto;
width: auto;
overflow: visible;
z-index: 9999;
outline: 3px solid #ffbf47;
}tabindex="-1" on the Target?Some browsers refuse to move keyboard focus to a non-interactive element (like <main> or a <div>) when a hash link is followed. Adding tabindex="-1" makes the element programmatically focusable so the browser places focus there reliably. Without it, the URL changes and the page may scroll, but the next Tab press takes the user back to the header — defeating the entire purpose of the skip link.
display: none?Elements set to display: none or visibility: hidden are removed from the focus order. The skip link must remain focusable so the keyboard user can reach it on the first Tab press — that is the whole reason for it to exist. Use clip-path, off-screen position: absolute, or the established "visually hidden" class instead.
Skip-link defects are easy to introduce and easy to verify. Three minutes of testing per template covers it.
tabindex="-1" on the target, aria-hidden on focus targets, and skip-link patterns that use display: none. Each finding ships with an AI-generated fix or one-click GitHub PR.<main>.The skip link must be the first interactive element inside <body>. If anything focusable precedes it (a logo link, a search field, a language switcher), the user has to Tab past those things first — which defeats the purpose.
"Skip to main content" is the standard. It tells the user exactly where they will land. "Skip" alone is ambiguous — and breaks under voice-control commands that need a specific target name.
tabindex="-1" to the TargetWithout it, some browsers won't move keyboard focus to <main> or any non-interactive container. The URL changes, the viewport scrolls, but the next Tab press goes back to the header.
clip-path, Not display: nonedisplay: none and visibility: hidden remove the element from the focus order. Use clip-path: inset(50%), position: absolute; left: -9999px, or the standard sr-only/visually-hidden class.
:focusWhen the link receives focus, make it impossible to miss: high-contrast colours, prominent position, and a visible outline. WCAG 2.4.7 Focus Visible (AA) applies here.
aria-hidden Off the TargetAn aria-hidden="true"element will accept focus but the screen reader won't announce it — silent failure. The target must be reachable to assistive tech.
On pages with major secondary navigation (like a docs sidebar), a second link such as "Skip to search" or "Skip to footer" is fine — but each must point to a distinct, useful target. Don't add them just to look thorough.
onClick DivsA skip link is in-page navigation. Use <a href="#main-content">. Browsers handle hash-based focus and scroll behaviour for free, and assistive tech recognises the link semantics.
In single-page apps, the same skip-link pattern still works — but you also need to call .focus() on the <main tabindex="-1"> element after each route transition so screen readers re-orient to the new page.
NVDA on Windows, VoiceOver on macOS/iOS, or TalkBack on Android. Automated checks miss subtleties — like a target that visually receives focus but is announced as empty because the screen reader can't find an accessible name.
What's happening: the page has no bypass mechanism. Every keyboard user has to Tab through the entire header and primary navigation on every page. WCAG 2.4.1 fails outright.
Fix: add the standard pattern — first focusable element in <body>, links to #main-content, with <main id="main-content" tabindex="-1"> as the target.
tabindex="-1"What's happening: activating the skip link changes the URL and scrolls the viewport, but focus stays in the header. The next Tab press takes the user right back where they started.
Fix: add tabindex="-1" to the target element. This is the single most common skip-link bug.
display: noneWhat's happening: the link is invisible — but it's also unreachable. Browsers exclude display: none elements from the focus order, so Tab skips it entirely.
Fix: swap to clip-path: inset(50%) or absolute off-screen positioning, paired with a :focus rule that brings the link into view.
What's happening: a designer added "Skip to content", "Jump to main", and "Skip header" — all pointing to #main-content. Only the first is useful; the others add Tab stops without value.
Fix: keep one skip link to main content. If you genuinely have multiple useful targets (search, footer, sidebar), give each a distinct destination and label.
Side-by-side reference for the most common implementation choices, and which one to reach for.
| Pattern | Visible by default? | Focusable? | Recommended? |
|---|---|---|---|
display: none until focus | No | No — removed from focus order | No. The link can never be reached. |
visibility: hidden until focus | No | No — also removed from focus order | No. Same problem as display: none. |
clip-path: inset(50%) until focus | No | Yes | Yes. Modern, robust, screen-reader friendly. |
position: absolute; left: -9999px | No | Yes | Yes. Older but widely supported "off-screen" pattern. |
| Always visible at the top | Yes | Yes | Acceptable if your design tolerates it. Government sites often choose this. |
A skip link is a hidden-until-focused in-page link, placed at the top of every page, that lets keyboard users jump past repeated content (header, navigation, banners) and land directly on the main content area. It is the standard mechanism for satisfying WCAG 2.2 SC 2.4.1 Bypass Blocks (Level A).
It must be the first focusable element inside <body>. Anything focusable that precedes it — a logo link, a search input, a language picker — defeats the purpose, because the user has to Tab past those before reaching the skip link.
tabindex="-1"?Some browsers refuse to move keyboard focus to a non-interactive element (like <main>) when a hash link is activated. tabindex="-1" makes the element programmatically focusable, which guarantees focus actually lands there and the next Tab press continues from inside the main content.
display: none?No. display: none and visibility: hidden remove the element from the keyboard focus order entirely, so the user can never reach it. Use clip-path: inset(50%), off-screen position: absolute, or the standard sr-only / visually-hidden utility class instead.
Yes. Screen-reader users can navigate by landmark or heading shortcuts, but the skip link is still useful — especially on the first page load before the user has entered focus mode and started using screen-reader navigation keys. WCAG 2.4.1 Bypass Blocks applies regardless of whether other navigation aids exist.
Only if each one points to a genuinely distinct, useful target. "Skip to main content", "Skip to search", and "Skip to footer" can coexist on a page where each destination is meaningful. Don't add multiple links pointing to the same target — only the first one is useful and the rest add unnecessary Tab stops.
Indirectly. Generative search systems most often surface pages that already rank well in traditional search, and Google factors accessibility and Page Experience signals into ranking. A page that fails WCAG 2.4.1 is more likely to underperform in search and therefore less likely to be sourced by Google AI Overviews or chat-based answer engines.
A skip link is the smallest piece of HTML that can satisfy a Level A WCAG criterion: one anchor at the top of <body>, one tabindex="-1" on the target, and a CSS rule that hides it until focus. That tiny pattern saves keyboard users dozens of keystrokes per page and makes voice-control and switch-device users dramatically more efficient.
Run a Greadme deep scan to check every page template for missing skip links, missing tabindex="-1" targets, and CSS that accidentally removes the link from focus order — each issue paired with an AI-generated fix or a one-click GitHub PR.