Accessible Skip to Content Link — Free HTML CSS JS Snippet

Accessible Skip to Content Link · Navigation · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

True off-screen technique: position: absolute; top: -48px keeps the link in the tab order and accessibility tree
Never uses display: none or visibility: hidden, which would remove the link from keyboard reach entirely
Focus-triggered reveal: .skip-link:focus animates top back into the viewport via CSS transition
High-contrast focus style: dark background plus a 3px solid outline so sighted keyboard users see it clearly
Programmatic focus jump: mainContent.focus() moves real keyboard focus, not just the URL hash
tabindex="-1" on #main-content makes a non-interactive landmark a valid, non-tab-order focus target
Realistic nine-link nav demonstrates the actual keystroke cost the skip link eliminates
Live status line confirms exactly when the link is focused versus activated for demo clarity

About this UI Snippet

Accessible Skip to Content Link — WCAG Skip-Navigation Pattern with Focus-Triggered Visibility

Screenshot of the Accessible Skip to Content Link snippet rendered live

A skip-to-content link is one of the oldest and most consequential patterns in web accessibility, and it is also one of the most commonly implemented incorrectly. Its job is simple: let a keyboard user or screen-reader user bypass a page's repeated navigation, header, and search widgets and jump straight to the unique content of the page, without having to tab through every menu item first. WCAG 2.2 Success Criterion 2.4.1 (Bypass Blocks) requires exactly this mechanism, and it is one of the first things an accessibility audit checks. This snippet implements the pattern correctly: a link that is invisible until it receives keyboard focus, then appears clearly at the top of the viewport with a high-contrast, unmistakable focus style.

The bug class this snippet avoids

The single most common mistake in skip-link implementations is hiding the link with display: none or visibility: hidden. Both properties remove an element from the accessibility tree entirely — a screen reader will never announce it, and more critically, a hidden element with display: none cannot receive focus at all, so a sighted keyboard user pressing Tab will simply skip over it as if it does not exist. The link becomes permanently unreachable rather than temporarily hidden, which defeats its entire purpose while still satisfying a naive "is there a skip link in the HTML" checklist item. This is exactly the kind of accessibility bug that passes a cursory code review and fails every real assistive-technology test.

The correct technique: absolute positioning off-screen

This snippet instead uses position: absolute with top: -48px to push the link above the visible viewport while keeping it fully present in the layout and, crucially, in the tab order and accessibility tree. Because the element is still rendered (just positioned outside the visible area), a screen reader announces it in document order exactly like any other link, and pressing Tab from the top of the page moves focus to it normally. The reveal happens entirely through the :focus pseudo-class: .skip-link:focus { top: 12px; } animates the link back into the viewport with a transition: top 0.2s ease, paired with a strong outline: 3px solid #6366f1 so sighted keyboard users get an unmistakable visual cue that focus has landed somewhere new. The moment focus moves away — either by tabbing onward or activating the link — it slides back off-screen.

Why the destination needs `tabindex="-1"`

Activating the skip link needs to do more than change the URL hash; it needs to move actual keyboard focus to the main content landmark so the very next Tab press continues from there, not from the top of the page again. The <main id="main-content" tabindex="-1"> element is not naturally focusable — only interactive elements like links, buttons, and form fields are — so tabindex="-1" is added specifically to make it a valid, though not tab-reachable, focus target. The JavaScript calls mainContent.focus() on click, which combined with the browser's native hash-jump from href="#main-content" produces the complete bypass behaviour: the viewport scrolls to the content and keyboard focus lands there simultaneously.

Why this matters for 2026 accessibility-first design

Accessibility-first design is no longer a compliance checkbox tacked on before launch — regulatory pressure (the European Accessibility Act, ADA Title II digital rules, WCAG 2.2 as the emerging baseline) and genuine product quality expectations have made keyboard and screen-reader usability a first-class design requirement from day one. A skip link is one of the highest-leverage accessibility investments a page can make: it costs almost nothing to build correctly, and it directly determines whether keyboard and assistive-technology users can efficiently use a site with a large navigation, or whether every single page load costs them dozens of extra keystrokes. Getting the off-screen mechanic right, rather than reaching for display: none, is the difference between a pattern that looks done and one that actually works.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to trace exactly why position: absolute with a negative top offset keeps the link keyboard-reachable while display: none would not — it's a great way to actually internalize the accessibility-tree distinction rather than memorizing a rule. You can also ask it to extend the pattern to a second "Skip to footer" link stacked below the first, or to adapt the reveal animation to a slide-from-left instead of slide-from-top while preserving the same off-screen/focus mechanics. Another good ask: have it audit a real page of yours for elements hidden with display: none or visibility: hidden that should instead be visually-hidden-but-focusable, since that exact mistake is the most common cause of broken skip links and broken accessibility labels in production codebases. Treat this less as a copy-paste widget and more as the reference case for a pattern that recurs throughout accessible UI work.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a WCAG-compliant "skip to main content" link in plain HTML, CSS, and JavaScript.

Requirements:
- The link must be the first focusable element in the document, placed before any header or navigation markup.
- It must remain fully present in the DOM and in the natural tab order at all times — do not use display: none or visibility: hidden at any point, since both remove an element from the accessibility tree and make it unfocusable.
- Instead, hide it visually by positioning it off-screen (for example position: absolute with a negative top or left offset) while it is unfocused, and reveal it with a smooth CSS transition when it receives keyboard focus via the :focus pseudo-class.
- The revealed state must have a clearly visible, high-contrast focus indicator (background, text color, and outline) that meets accessible contrast ratios against the page background.
- Clicking or activating the link (Enter key while focused) must move actual keyboard focus to the main content landmark, not just scroll to it — give the landmark id="main-content" and tabindex="-1" so it becomes a valid programmatic focus target, and call element.focus() on it in JavaScript.
- Build a small demo page around it with a realistic multi-item navigation bar before the main content, so the practical benefit of skipping it is obvious.
- Add a visible status indicator in the demo that confirms when the skip link has actually been activated versus merely focused, for clarity during manual testing.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Click into the preview, then press TabClick anywhere on the light grey preview background first so the demo iframe has focus, then press Tab once. The "Skip to main content" link slides down from off-screen into view at the top-left with a high-contrast dark background and a visible indigo focus outline.
  2. 2
    Activate the link to jump past the navPress Enter (or click the visible link) while it is focused. Focus moves directly to the #main-content landmark — notice the status line updates and the page does not require tabbing through the nine nav links above it to get there.
  3. 3
    Understand the CSS mechanicInspect .skip-link in the CSS panel: it uses position: absolute; top: -48px to sit outside the viewport while remaining in the DOM and tab order. The .skip-link:focus rule sets top: 12px, animated by transition: top 0.2s ease, to bring it into view only when it actually has focus.
  4. 4
    Place it as the very first focusable elementA skip link only works if it is the first tabbable element on the page — before the logo, before the nav, before any cookie banner. In the HTML panel it sits immediately as the first child, before .demo-header, which is exactly where it must go in your real layout.
  5. 5
    Add tabindex="-1" to your real main landmarkIn your own app, add id="main-content" tabindex="-1" to the <main> element (or the first heading inside it). Without tabindex="-1", calling mainContent.focus() in JS silently does nothing in most browsers because non-interactive elements are not programmatically focusable by default.
  6. 6
    Export and adapt for multi-region pagesClick HTML or JSX to export. For pages with multiple landmarks (nav, main, footer), consider adding a second skip link ("Skip to footer") using the same off-screen/focus pattern, stacked with a slightly larger top offset so both remain reachable and visually distinct when tabbed to in sequence.

Real-world uses

Common Use Cases

Fixing a "fake" skip link that never appears on Tab
Many sites ship a skip link that technically exists in the HTML but was hidden with display: none for visual tidiness, which silently makes it unreachable by keyboard. This snippet is a direct, side-by-side reference for the correct pattern — swap any display: none or visibility: hidden skip-link CSS in an existing codebase for the position: absolute plus :focus technique shown here to make the link genuinely reachable again.
Baseline accessibility requirement for marketing sites and web apps
WCAG 2.2 Success Criterion 2.4.1 (Bypass Blocks) requires a mechanism to skip repeated navigation on every page with more than a trivial header. Add this component as the very first element inside your root layout, right after the opening body tag, so it applies site-wide with zero per-page setup.
Complex dashboards and SaaS apps with deep sidebar navigation
Applications with long sidebars, multi-level menus, or persistent top toolbars impose an especially high keyboard-navigation tax without a skip link — sometimes 20 or more tab stops before reaching the actual page content. Point the skip link at the primary content region of your app shell so power users and assistive-technology users can bypass the chrome and get to work immediately, similar in spirit to the Accessible Skip to Content Link pattern applied to a persistent app frame.
Multi-landmark pages needing more than one skip target
Long pages with a nav, a filter sidebar, and a footer full of links benefit from multiple skip links ("Skip to main content", "Skip to filters", "Skip to footer"), each using the same off-screen/focus-reveal CSS technique but targeting a different tabindex="-1" landmark, stacked vertically so each becomes visible in turn as the user tabs through them.
Accessibility audits and automated testing pipelines
Automated tools like axe-core or Lighthouse flag missing or non-functional skip links, but they cannot always verify that a link is genuinely keyboard-reachable versus merely present in markup. Use this snippet as the reference implementation when writing a manual keyboard-only test pass, or as the fix template when an audit surfaces a "bypass blocks" violation on an existing page.
Teaching the difference between visually hidden and accessibility-hidden
This is a canonical teaching example for the distinction between elements that are visually hidden but accessible (off-screen positioning, or the common .sr-only utility class) versus elements that are hidden from everyone including assistive technology (display: none, visibility: hidden, or the hidden attribute). Understanding this distinction correctly prevents an entire category of accessibility regressions across a codebase, not just in skip links.

Got questions?

Frequently Asked Questions

display: none removes an element from the accessibility tree and, critically, makes it impossible to focus in the first place — a keyboard user tabbing through the page would skip right over it because there is nothing there to receive focus. You cannot use JavaScript to detect a focus event that can never fire. Off-screen positioning with position: absolute keeps the link fully present and focusable at all times; only its visual location changes on :focus.

Only naturally interactive elements (links, buttons, inputs, elements with a native tabindex) can receive keyboard focus by default. A <main> or <div> landmark is not interactive, so calling element.focus() on it does nothing in most browsers unless it has tabindex="-1", which makes it programmatically focusable without adding it to the natural Tab order. This lets the skip link move real focus there, so the next Tab press continues from inside the content rather than jumping back to the top of the page.

Yes — its entire value depends on being the first (or one of the first) tabbable elements a keyboard user encounters. If it appears after the logo, search box, or any other focusable element, users still have to tab past those first, which defeats the purpose. Place it immediately inside the opening body tag, before any header, nav, or cookie-consent banner markup.

Landmark regions help screen-reader users navigate by region using rotor or landmark-jump commands, but they do nothing for sighted keyboard-only users who rely purely on the Tab key, and not every screen reader user relies on landmark navigation exclusively. A visible, focusable skip link is still required by WCAG 2.4.1 and remains the most universally supported bypass mechanism across assistive technologies and keyboard-only use.

Yes — any colours work as long as the focused state maintains strong contrast against the page background (WCAG requires at least 3:1 contrast for the focus indicator itself and 4.5:1 for the link text against its own background). Keep the position: absolute plus top offset plus :focus reveal structure intact; only the background, text colour, border-radius, and outline colour are safe to customise.