Keyboard Accessibility

Implementing Skip Links

10 min read

Skip links allow keyboard users to bypass repetitive content (like navigation menus) and jump directly to the main content. They're one of the simplest yet most impactful accessibility features you can add to any website.

Consider a website with 20 navigation links. Without a skip link, keyboard users must tab through all 20 links on every page before reaching the content. Skip links solve this problem.

WCAG Requirement

WCAG 2.4.1 Bypass Blocks (Level A): Mechanisms must be available to bypass blocks of content repeated on multiple pages.

Skip links are the most common way to meet this requirement.

html
<body>
  <a href="#main-content" class="skip-link">
    Skip to main content
  </a>
  
  <header>
    <nav>
      <!-- Navigation with many links -->
    </nav>
  </header>
  
  <main id="main-content" tabindex="-1">
    <h1>Page Title</h1>
    <!-- Main page content -->
  </main>
</body>

Skip links should be:

  1. Visually hidden by default
  2. Visible when focused
  3. High contrast for visibility
css
.skip-link {
  position: absolute;
  top: -100%;
  left: 16px;
  padding: 12px 16px;
  background-color: #000000;
  color: #ffffff;
  font-weight: 600;
  text-decoration: none;
  border-radius: 4px;
  z-index: 9999;
  transition: top 0.2s ease;
}

.skip-link:focus {
  top: 16px;
}

Tailwind CSS Version

jsx
<a
  href="#main-content"
  className="
    sr-only
    focus:not-sr-only
    focus:absolute
    focus:top-4
    focus:left-4
    focus:z-50
    focus:px-4
    focus:py-2
    focus:bg-primary
    focus:text-primary-foreground
    focus:rounded-md
    focus:font-medium
  "
>
  Skip to main content
</a>

For complex pages, provide multiple skip links:

html
<div class="skip-links">
  <a href="#main-content">Skip to main content</a>
  <a href="#main-nav">Skip to navigation</a>
  <a href="#search">Skip to search</a>
  <a href="#footer">Skip to footer</a>
</div>
css
.skip-links {
  position: absolute;
  top: -100%;
  left: 16px;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.skip-links:focus-within {
  top: 16px;
}

.skip-links a {
  padding: 12px 16px;
  background: #000;
  color: #fff;
  text-decoration: none;
  border-radius: 4px;
}

.skip-links a:focus {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

React Implementation

jsx
function SkipLink({ href, children }) {
  return (
    <a
      href={href}
      className="skip-link"
    >
      {children}
    </a>
  );
}

function Layout({ children }) {
  return (
    <>
      <SkipLink href="#main-content">Skip to main content</SkipLink>
      <Header />
      <main id="main-content" tabIndex={-1}>
        {children}
      </main>
      <Footer />
    </>
  );
}

Next.js/App Router Implementation

tsx
// app/layout.tsx
import './skip-link.css';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <a href="#main-content" className="skip-link">
          Skip to main content
        </a>
        <Header />
        <main id="main-content" tabIndex={-1}>
          {children}
        </main>
        <Footer />
      </body>
    </html>
  );
}

Important Implementation Details

1. Target Element Must Be Focusable

html
<!-- The target needs tabindex="-1" to receive focus -->
<main id="main-content" tabindex="-1">
  ...
</main>

Without tabindex="-1", some browsers won't actually move focus to the element.

2. Remove Focus Outline on Target

css
/* Prevent ugly outline on main when skip link used */
main:focus {
  outline: none;
}

3. Handle Single-Page Apps

jsx
// Reset focus on route change
useEffect(() => {
  const mainContent = document.getElementById('main-content');
  if (mainContent) {
    mainContent.focus();
  }
}, [pathname]);
  1. Load the page and press Tab: Skip link should appear
  2. Press Enter on skip link: Focus moves to main content
  3. Continue tabbing: Focus should be after navigation
  4. Screen reader test: Skip link announced correctly

Common Mistakes

Mistake 1: Skip link that doesn't actually skip

html
<!-- Bad: Just scrolls, doesn't move focus -->
<a href="#main-content" onclick="smoothScroll('#main-content')">Skip</a>

<!-- Good: Focus actually moves -->
<a href="#main-content">Skip to main content</a>
<main id="main-content" tabindex="-1">...</main>

Mistake 2: Skip link hidden from screen readers

css
/* Bad: Hidden from everyone */
.skip-link {
  display: none;
}

/* Good: Visually hidden but accessible */
.skip-link {
  position: absolute;
  top: -100%;
}
.skip-link:focus {
  top: 16px;
}
  • Skip link is the first focusable element on the page
  • Link text clearly describes the action
  • Target element has tabindex="-1"
  • Link becomes visible on focus
  • High contrast colors when visible
  • Tested with keyboard navigation
  • Tested with screen readers

Was this article helpful?