# Skip links must point to a valid target on the page.

> Canonical HTML version: https://rocketvalidator.com/accessibility-validation/accesslint/0.16/navigable/skip-link
> Attribution: Rocket Validator (https://rocketvalidator.com)
> License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/)

A skip link is a navigation aid that lets users jump past repeated content, such as a site's main navigation menu, and land directly on the primary content of the page. When a skip link's `href` attribute points to an `id` that does not exist on the page, the link does nothing. The user clicks it, and nothing happens.

This affects keyboard users and screen reader users most directly. Sighted mouse users can scroll past navigation without thinking about it, but a keyboard user must press `Tab` through every single link in a navigation bar before reaching the main content. On a site with dozens of navigation links, this is tedious and slow. A working skip link removes that burden by moving focus to the main content area in a single keypress.

Screen reader users face the same problem. They hear every navigation item announced as they move through the page. A skip link that actually works lets them bypass all of that and get straight to the content they came for.

This rule maps to WCAG 2.4.1 Bypass Blocks (Level A). That success criterion requires that a mechanism exists for bypassing blocks of content that are repeated across multiple pages. A skip link is the most common way to satisfy this requirement, but it only works if the link target is valid. A broken skip link fails the criterion because the bypass mechanism is non-functional.

## How to fix it

1. Make sure the skip link's `href` value (e.g., `#main`) matches the `id` attribute of an element on the page.
2. The target element should be the container for the page's main content, or the `<main>` element itself.
3. Place the skip link as the first focusable element in the page's DOM order, before any navigation or header links.
4. The skip link can be visually hidden by default and revealed when it receives focus, using CSS. This keeps it out of the visual layout for sighted mouse users while remaining available to keyboard users.
5. On the target element, add `tabindex="-1"` if it is not natively focusable (like a `<div>` or `<main>` element). Some browsers do not move focus to non-focusable elements even when the fragment identifier matches, so `tabindex="-1"` ensures consistent behavior.

## Examples

### Skip link with a missing target

The link points to `#main-content`, but no element on the page has that `id`. Activating the link does nothing.

```html
<body>
  <a href="#main-content" class="skip-link">Skip to main content</a>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </nav>
  <main>
    <h1>Welcome</h1>
    <p>Page content goes here.</p>
  </main>
</body>
```

### Skip link with a valid target

The `href="#main-content"` matches the `id="main-content"` on the `<main>` element. The `tabindex="-1"` attribute makes the target programmatically focusable.

```html
<body>
  <a href="#main-content" class="skip-link">Skip to main content</a>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </nav>
  <main id="main-content" tabindex="-1">
    <h1>Welcome</h1>
    <p>Page content goes here.</p>
  </main>
</body>
```

### CSS to visually hide the skip link until focused

This approach keeps the skip link off screen until a keyboard user tabs to it. When focused, it appears at the top of the page.

```css
.skip-link {
  position: absolute;
  left: -9999px;
  top: auto;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.skip-link:focus {
  position: fixed;
  top: 0;
  left: 0;
  width: auto;
  height: auto;
  padding: 8px 16px;
  background: #000;
  color: #fff;
  z-index: 1000;
}
```

## Common mistakes

A few patterns cause this rule to trigger:

- The `href` has a typo, such as `href="#maincontent"` when the element's `id` is `main-content`.
- The target element exists in a template or component that has not yet rendered when the skip link is evaluated.
- The `id` attribute was removed during a refactor but the skip link still references it.
- The skip link uses `href="#"` or `href=""`, which does not point to any specific element.

The fix is always the same: make sure the `href` fragment matches an actual `id` on the page, and confirm that the target element is present in the DOM when the page loads.
