Fix

No skip link past your store header

A skip link is the shortcut that lets someone jump past your promo bar, logo, search field and mega-menu straight into the products. Without one, they tab through the lot — on every page, including checkout.

Auto-applied WCAG 2.4.1

Who this locks out

Without a skip link, a keyboard user tabs through the whole header on every page.

  • Keyboard-only shoppers with a tremor or limited hand movement
  • Screen-reader users meeting the same mega-menu on every page
  • Switch and voice-control users, for whom each tab stop is a command

The transform

Before

<body class="woocommerce-shop"><header class="site-header">

After

<body class="woocommerce-shop"><a id="klarvo-skip-link" class="klarvo-skip-link" href="#klarvo-main">Skip to content</a><header class="site-header">

Detection

How this is tested here

Every scan runs three independent engines and nine custom probes against your rendered pages, and records which engine flagged what. These are the real rules behind this fix family.

WCAG success criteria this family covers, and the engine rules that detect them. Generated from the engine's own tables — never edited by hand.
Criterion Level Detected by What we do
2.4.1 Bypass Blocks A axe-core (bypass) · IBM Equal Access (aria_application_label_unique, aria_application_labelled, aria_article_label_unique) auto

What Klarvo Access does

Klarvo generates the bypass link, applies it server-side in the HTML your store serves, and then re-validates the page independently to confirm the link is present and its target resolves.

Auto-applied

Generated, then applied server-side in the HTML your store actually serves — and independently re-validated in the served page before it counts as resolved.

An automated finding is evidence, not a certificate. Conformance is certified only by the professional audit — how that works.

Do it yourself

Fixing this by hand

Every page of your store opens with the same furniture: the delivery-promise bar, the logo, the search field, the mega-menu of categories, the account link, the basket. A mouse user’s eye skips it. A keyboard user cannot: they press Tab, and Tab, and Tab, through every link in that header, before focus reaches the first product. Then they open a product, and the header is back. Then the basket. Then checkout, where it sits above the fields that take their money.

WCAG 2.4.1 Bypass Blocks (Level A) asks for a way past content that repeats across pages. The usual answer is a skip link: an anchor placed first in the page that moves focus into the main content. It is a few lines of markup, and the cost of leaving it out falls entirely on the people least able to absorb it:

  • Someone with a tremor, arthritis or a repetitive strain injury, for whom forty extra key presses per page is pain, not inconvenience.
  • A screen-reader user who listens past the same category list on every page before hearing anything about the product they came for.
  • A switch user or someone driving the browser by voice, where every tab stop is a separate press or spoken command.

The shopper who needs fifty keystrokes to reach your payment fields is the shopper who abandons the basket, and you never see why. A skip link removes a tax that only some of your customers are paying.

How it is detected here

Two of the three engines in the scan carry a named rule for this criterion, so the finding is cross-engine, not one vendor’s opinion.

  • axe-core 4.12.0 reports it through the rule bypass.
  • IBM Equal Access (ace, vendored) reports it through html_skipnav_exists, skip_main_exists and skip_main_described. Mapped to the same criterion there, too: aria_application_labelled, aria_application_label_unique, aria_article_label_unique, aria_document_label_unique, aria_toolbar_label_unique and frame_src_valid.
  • HTML_CodeSniffer, the third engine, mentions 2.4.1 in its output but exposes no discrete rule id for it, so we do not count it as coverage.

No custom Klarvo probe is registered for 2.4.1; the named rules above already cover it. What Klarvo adds is a deterministic check over the raw HTML your server sends: it looks for a same-page link whose target id genuinely exists in that document. A link pointing at #content where nothing carries id="content" is not a bypass mechanism — a common breakage after a theme migration or a page-builder rebuild.

One honest limitation: axe’s bypass rule is satisfied by a main landmark alone, so a page can pass it while offering a sighted keyboard user no shortcut. That is why the fix plants a real link rather than stopping at the landmark, and why the check below uses the Tab key.

What Klarvo does about it

This family is auto tier. The fix is generated, applied server-side in the HTML your store serves — never painted on by a script in the visitor’s browser — and then independently re-validated on a fresh fetch of the page.

What gets applied is the transform at the top of this page: the anchor is inserted immediately after the opening <body> tag, and the focus styling that reveals it is injected once. The target is chosen, not guessed:

  • If your <main> element already has an id, that id becomes the link target and nothing else is touched.
  • If <main> has no id, id="klarvo-main" is added to it as a separate rule.
  • If the page has no <main> at all, the engine will not guess which wrapper is “the content”. That page goes to a human review queue instead of receiving an invented target.

The operation is idempotent: a page already carrying the anchor or its style block never gets a second copy, and a theme that already prints a working skip link is left alone. An automated finding is evidence, not a certificate — so after applying, the page is fetched and scanned again, and the finding has to clear on that second look before it is recorded as fixed.

How to fix it yourself

A skip link is a genuinely quick fix by hand — three small steps.

1. Give the content wrapper an id and make it focusable.

<main id="main-content" tabindex="-1">

tabindex="-1" matters: when the target of a same-page link is not focusable, some browsers scroll the page but leave keyboard focus where it was, so the next Tab press drops the visitor back into the header they were escaping. A negative tabindex makes the container focusable programmatically without adding it to the tab sequence.

In a classic theme the wrapper lives in the template files: search for <main, id="content" or id="primary" in header.php, page.php and WooCommerce’s archive-product.php. Edit a child theme, never the parent.

2. Put the link first inside the body. The cleanest hook is wp_body_open, which fires immediately after the opening <body> tag. In your child theme’s functions.php:

add_action( 'wp_body_open', function () {
	printf(
		'<a class="skip-link" href="#main-content">%s</a>',
		esc_html__( 'Skip to content', 'your-child-theme' )
	);
}, 1 );

Priority 1 puts your link ahead of anything else hooked there — cookie banners, promo bars and store notices all want that position. The hook exists from WordPress 5.2, but in a classic theme it fires only if header.php calls wp_body_open(). Grep for it; if it is missing, add it directly after the <body> tag in your child theme’s header.php.

3. Hide it until it is focused — and only that way.

.skip-link {
	position: absolute;
	left: -9999px;
	top: auto;
	overflow: hidden;
}

.skip-link:focus {
	position: fixed;
	left: 12px;
	top: 12px;
	z-index: 100000;
	padding: 10px 16px;
	background: #047756;
	color: #fff;
	border-radius: 8px;
	text-decoration: none;
	outline: 2px solid #047756;
	outline-offset: 2px;
	overflow: visible;
}

Never hide a skip link with display: none, visibility: hidden or the hidden attribute: all three remove it from the tab order, leaving a link in your source that nobody can reach. Many WordPress themes ship a screen-reader-text class that handles this correctly; if you reuse it, tab to the link and confirm it becomes visible — page-builder copies of that class routinely omit the :focus rule.

4. Block themes and the Site Editor. A block theme has no header.php, but core renders the page shell and fires wp_body_open there, so the snippet above works unchanged in a block child theme. For the target: open the template in the Site Editor, select the Group block that wraps your content and, in its Advanced panel, set the HTML element to main and the HTML anchor to main-content — that renders as <main id="main-content">.

WooCommerce pages worth testing separately. The Cart and Checkout blocks render inside the normal content wrapper, so one correct target serves them — but a “distraction-free” checkout template often ships its own header markup, so walk it by keyboard rather than assuming. On shop and category archives the filter sidebar is repeated content too: a second link labelled “Skip to products” is legitimate, and W3C’s technique G123 covers that pattern — skipping a block rather than jumping to main. If a sticky header covers the target after the jump, give it breathing room:

#main-content { scroll-margin-top: 96px; }

How you know it worked

  1. Open your shop page in a new tab, click into the address bar, then press Tab once. The first element to take focus should be the skip link, and you should be able to see it.
  2. Press Enter, then press Tab again. The next focus stop must be inside the content. If it lands back in the header, the target is not focusable — add tabindex="-1".
  3. Repeat on a category archive, a product page, the basket and the checkout. A fix in header.php or on wp_body_open covers all of them; a fix pasted into one template does not.
  4. View source and confirm the target id appears exactly once. Duplicate ids survive theme migrations quietly, and the jump then lands in the wrong place.
  5. Re-scan. bypass and skip_main_exists should both pass — and remember that bypass alone would also pass on a landmark, so the keyboard walk above is the real proof.
  6. Spot-check with a screen reader: VoiceOver (Cmd+F5) or NVDA should announce “Skip to content, link” as the first item on the page.

Sources: W3C WAI — Understanding SC 2.4.1: Bypass Blocks · W3C WAI — Technique G1: Adding a link at the top of each page that goes directly to the main content area · W3C WAI — Technique G123: Adding a link at the beginning of a block of repeated content to go to the end of the block · W3C WAI — Technique ARIA11: Using ARIA landmarks to identify regions of a page · Deque University — axe rule bypass (axe-core 4.12) · WordPress Documentation — Group block (Advanced: HTML element, HTML anchor)

FAQ

Questions this raises

Will a skip link change how my shop looks?

No. It is positioned off-screen until someone moves keyboard focus to it, and it appears only for that person, in that moment. A mouse user will never see it.

My theme already prints a skip link. Will Klarvo add a second one?

No. The check looks for a same-page link whose target id actually exists in the served HTML; if one is already there the page is left alone, and the injected anchor is never doubled on a page that already carries it.

Our scan passed because the page has a main landmark. Is that enough?

It is enough for the automated rule and not enough for the shopper. A landmark gives screen-reader users a jump; it gives a sighted keyboard user nothing. W3C lists the visible link at the top of the page as a sufficient technique in its own right, so add it.

Find out whether your store has this one.

The free scan checks your real pages — home, product, populated basket, hydrated checkout — with all three engines and all nine probes, and tells you exactly what it found and where.