Add a skip link that lands on the main region

The problem

On a page with dozens of header links, a keyboard reader must tab through all of them to reach the content, and a hidden skip link that never appears on focus does not help anyone reach it.

Short answer

Make the link the first focusable element, point it at the main region's id, reveal it on focus instead of hiding it off-limits, and give the target a tabindex of -1.

A skip link is three pieces that have to be right together: position, target, and reveal.

Language: HTML
<body>
  <a class="skip-link" href="#main">Skip to main content</a>

  <header>
    <nav aria-label="Primary">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/search/">Search</a></li>
      </ul>
    </nav>
  </header>

  <main id="main" tabindex="-1">
    <h1>Practical developer examples</h1>
  </main>
</body>

Language: CSS
.skip-link {
  position: absolute;
  inset-inline-start: 0.75rem;
  inset-block-start: -4rem;
  padding: 0.75rem 1rem;
  background: #ffffff;
  color: #1b1c20;
  border: 1px solid #454953;
  border-radius: 0.5rem;
  z-index: 5;
}

/* Reveal on focus only. Never display:none, which removes it from the tab order. */
.skip-link:focus-visible {
  inset-block-start: 0.75rem;
  outline: 3px solid #b35c00;
  outline-offset: 2px;
}

main:focus,
main:focus-visible {
  outline: none;
}

Explanation

The link must be the first focusable element in the document, because its whole purpose is to be reachable before the things it skips. Placing it after the header, or inside the header as the last item, means a keyboard reader meets the repeated navigation first and the mechanism arrives too late to help.

Off-screen positioning rather than display: none or visibility: hidden is the second requirement: both of those remove the element from the focus sequence entirely, so the shortcut exists in the markup but nobody can ever reach it. Moving the link into view on :focus-visible shows it only to the person who tabbed to it, which is why the visual design can be plain without bothering pointer users.

The tabindex="-1" on the target is the piece most often missed. Fragment navigation moves the document scroll position, but in some browsers a non-focusable container does not receive focus, so the next Tab press resumes from the top of the page and walks the header again — exactly the behaviour the link was supposed to avoid. tabindex="-1" makes it a programmatic focus target without adding it to the tab sequence, and the accompanying outline: none is acceptable here because the reader has just performed an explicit action to get there and the heading is now at the top of the viewport.

Assumptions

  • The page has one main element with a unique id, as described in the landmark example.
  • Text contrast between the link and its background meets 4.5:1 in both themes; the values above are the light-scheme pair.

Common mistakes

  • Hiding the link with display: none so it can never be focused, which turns the bypass into decoration.
  • Targeting a wrapper div that is not the content region, so the scroll lands in the wrong place and screen-reader position does not move.
  • Adding a skip link only for keyboard users of the header and leaving a long sidebar navigation before the content, which the link does not bypass.

Caveats

  • A skip link bypasses blocks of repeated content; it does not substitute for a heading structure, and WCAG expects both for deeply structured pages.
  • If your app re-renders the main region on route change, the focus target may be replaced and the browser can lose the position; restore focus deliberately in that case.

Follow-up

Related examples

Editorial links first, then deterministic same-task or same-topic candidates. Tags and shared language alone never qualify a candidate.

References