Test skip navigation links
A mechanism is available to bypass blocks of content that are repeated on multiple web pages. Skip links are the most common solution.
This is the main content that keyboard users want to reach quickly. Without a skip link, they must tab through 10+ navigation items first.
With and without skip link
Tab 1: Home link
Tab 2: Products link
Tab 3: Services link
Tab 4: About link
Tab 5: Blog link
Tab 6: Contact link
Tab 7: Search button
Tab 8: Notifications button
Tab 9: User menu button
Tab 10: Finally reach main content!
Tab 1: Skip link appears
Enter: Jump to main content!
Saves 8+ keystrokes per page
Essential for users with motor impairments
Reduces cognitive load
Required by WCAG 2.4.1 (Level A)
HTML and CSS code
<!-- Skip link as first focusable element -->
<body>
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<header>
<!-- Navigation items -->
</header>
<main id="main-content" tabindex="-1">
<!-- Main content -->
</main>
</body>
<style>
.skip-link {
position: fixed;
top: 0;
left: 0;
transform: translateY(-100%);
z-index: 9999;
}
.skip-link:focus {
transform: translateY(0);
}
</style>Testing hints
// Test skip link exists
const skipLink = page.getByRole('link', {
name: /skip to (main )?content/i
});
// Tab to reveal skip link
await page.keyboard.press('Tab');
await expect(skipLink).toBeVisible();
// Verify skip link works
await skipLink.click();
// Main content should have focus
const main = page.locator('#main-content');
await expect(main).toBeFocused();
// Or check with axe-core
import AxeBuilder from '@axe-core/playwright';
const results = await new AxeBuilder({ page })
.withRules(['bypass'])
.analyze();
expect(results.violations).toHaveLength(0);// Multiple skip links for complex pages
<nav aria-label="Skip links">
<a href="#main">Skip to main content</a>
<a href="#nav">Skip to navigation</a>
<a href="#search">Skip to search</a>
<a href="#footer">Skip to footer</a>
</nav>
// Target element requirements:
// 1. Has matching id attribute
// 2. Has tabindex="-1" for non-focusable elements
// 3. Receives focus on activation
// Test focus moves correctly
await page.keyboard.press('Tab');
await page.keyboard.press('Enter');
const focused = await page.evaluate(
() => document.activeElement?.id
);
expect(focused).toBe('main-content');Automation hints
page.keyboard.press('Tab')page.locator('#main-content')toBeFocused()withRules(['bypass'])