
How to Add a Skip-to-Content Link to a Shopify Theme (WCAG 2.4.1)
A skip-to-content link is a common technique for WCAG 2.1 SC 2.4.1 (Bypass Blocks, Level A), which requires a mechanism to bypass repeated blocks rather than prescribing one universal implementation. Theme versions and customizations differ. This guide provides an adaptable pattern for a store that lacks a working bypass mechanism.
What the skip link does
Skip-to-content links serve users who navigate by keyboard or assistive technology. Without one, a screen-reader user reaching your storefront has to listen through the full header (logo, search, cart, account, navigation, mega-menu, announcement bar) on every page before reaching the actual page content. With one, pressing Tab once focuses the skip link; pressing Enter jumps focus and screen-reader cursor directly to the main content.
The link is normally revealed when keyboard focus reaches it. The pattern is widely supported, but test it with the store's supported browsers and assistive technologies and do not assume mouse users can never encounter it.
The implementation
Step 1 — Add the link as the first focusable element
Open sections/header.liquid (Online Store 2.0 themes) or layout/theme.liquid (Online Store 1.0 themes) in the Shopify admin → Online Store → Themes → Edit code. Find the opening <header> tag and add the skip link immediately before it:
<a class="skip-to-content-link" href="#MainContent">
Skip to content
</a>
<header>
...
</header>
The href="#MainContent" should match the ID on the main content wrapper. In Dawn that wrapper is <main id="MainContent">. In other themes it might be <main id="main-content"> or similar — check your theme.liquid for the existing main element ID.
Step 2 — Add the visually-hidden + focus-visible CSS
Add this block to assets/base.css (or your theme's primary stylesheet):
.skip-to-content-link {
position: absolute;
top: 0;
left: 0;
z-index: 100;
padding: 0.75rem 1rem;
background: #0a0a0a;
color: #fff;
font-weight: 600;
text-decoration: none;
/* Hide visually but keep in the accessibility tree */
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
width: 1px;
overflow: hidden;
white-space: nowrap;
}
.skip-to-content-link:focus {
/* Reveal on keyboard focus */
clip: auto;
clip-path: none;
height: auto;
width: auto;
overflow: visible;
white-space: normal;
outline: 3px solid #f97316;
outline-offset: 2px;
}
The crucial detail is clip: rect(0 0 0 0) plus position: absolute — this hides the element visually but keeps it in the accessibility tree and focusable. Using display: none would remove it from the tab order entirely, defeating the purpose.
Step 3 — Verify the target exists and is focusable
The skip link only works if the target element can receive focus. Find your <main> element in layout/theme.liquid:
<main id="MainContent" tabindex="-1">
{{ content_for_layout }}
</main>
tabindex="-1" lets the element receive programmatic focus from the skip link without inserting it into the natural Tab order. Without tabindex="-1", Safari and some other browsers will scroll to the target but not move keyboard focus there — the screen-reader announcement misses.
Per-theme variations
| Theme | Already ships skip link? | Notes |
|---|---|---|
| Current or customized theme | Verify | Inspect the published version, focus behavior, link target, and styles. |
| Legacy theme copy | Verify | Historical versions and merchant edits differ; do not add a duplicate before testing. |
| App-provided shell or header | Coordinate | Identify code ownership and ask the vendor to remediate app-owned focus order or markup. |
Common mistakes to avoid
display: none instead of clipping
/* WRONG — removes from accessibility tree */
.skip-to-content-link {
display: none;
}
.skip-to-content-link:focus {
display: block;
}
display: none makes the element unreachable by Tab. Use the clip: rect(0 0 0 0) pattern instead.
Multiple skip links to the same target
Some themes ship a skip link in sections/header.liquid and another in layout/theme.liquid from a partial migration. Two skip links pointing at the same target confuse screen-reader users. Keep one.
Skip link hidden on mobile
A theme that hides the skip link via @media (max-width: 768px) { display: none } fails 2.4.1 on mobile. Mobile keyboard users (Bluetooth keyboards, switch-control devices) rely on it just as much as desktop.
Skip link below other focusable elements
The skip link must be the first focusable element in document order. If a "Cart" icon button precedes it, keyboard users hit Cart first and never reach the skip link. Move it above every other interactive element in header.liquid.
How to verify it works
- Open your storefront in any browser.
- Click in the URL bar to clear focus.
- Press Tab once. The skip link should appear in the top-left corner with a high-contrast outline.
- Press Enter. The page should scroll to (and focus on) the main content area.
- Press Tab again. The next focused element should be inside the main content, not back in the header.
If steps 3, 4, or 5 fail, check the corresponding section above.
Why this is a deterministic fix
A skip link has structural parts that automation can inspect, but correct focus movement and usefulness still require keyboard and assistive-technology testing. AccessComply can propose an eligible, safely source-mapped first-party change for merchant review through a supported theme path and run a post-change check. Re-test after material theme, header, or app changes.
Further reading
- WCAG 2.1 SC 2.4.1 Bypass Blocks — full understanding doc
- WebAIM: Skip Navigation Links — implementation patterns
- Shopify dev docs: Online Store 2.0 themes
- AccessComply: WCAG 2.4.1 Bypass Blocks reference
- AccessComply: Dawn theme accessibility audit
- AccessComply: keyboard navigation tester (free bookmarklet)
Find the storefront issues holding back growth
Scan SEO, speed, and accessibility by page. Review supported fixes before they run, keep saved originals, and verify the live result afterward.