Fix

Your theme is hiding the keyboard focus outline

Focus is the keyboard user's cursor — it marks where the next keystroke lands. A one-line CSS reset hides it, and a shopper who cannot see where they are cannot pick a delivery option, tick a box or pay.

Auto-applied WCAG 2.4.7

Who this locks out

outline: none makes the keyboard cursor invisible — nobody can see where they are.

  • Sighted keyboard-only shoppers with a tremor, RSI or limited hand movement
  • Shoppers with low vision, for whom a faint one-pixel ring was already invisible
  • Switch users, for whom the outline is the only confirmation a press landed

The transform

Before

<style>*:focus { outline: none; }</style>

After

<style id="klarvo-access-a11y-inline-css">:root{--klarvo-fix-profiles:1}:where(a,button,input,select,textarea,summary,[tabindex]):focus-visible{outline:3px solid #047756 !important;outline-offset:3px !important}</style>

Detection

How this is tested here

Every scan runs three independent engines and 19 custom checks 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.7 Focus Visible Most affects: People with motor impairments · People with low vision · People with ADHD AA IBM Equal Access (element_tabbable_visible, style_focus_visible) · Klarvo probe (klarvo.focus-visible) auto

What Klarvo Access does

Klarvo injects a constant, pre-audited focus outline profile server-side in the HTML your store serves — a contrast-checked ring with an offset — then re-validates that it is present and survives the theme CSS that suppressed the original.

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

What a hidden focus outline costs your store

Watch someone shop without a mouse. They press Tab and a ring appears around the search field: that ring is their cursor. It tells them where they are, what Enter will activate, where the next keystroke lands. Now remove the ring and press Tab again. The page does not change. Press it five more times — still nothing. Focus is somewhere, moving invisibly through your header, and the shopper is navigating your store from memory, pressing Enter on faith.

That is the entire failure. WCAG 2.4.7 Focus Visible (Level AA) asks for one modest thing: any keyboard-operable interface must have a mode where the focused element is visibly indicated. Browsers do this for free — every browser ships a default focus indicator, which is why the W3C’s baseline technique, G149, is simply to use components the user agent highlights itself. It takes a positive act of CSS to fail this criterion. The act in question is almost always the same line:

*:focus { outline: none; }

A decade of design tutorials called the default ring ugly, and the reset shipped everywhere — theme frameworks, page-builder defaults, copied boilerplate in Additional CSS. The W3C catalogues it as failure F78: styling outlines and borders in a way that removes or hides the visual focus indicator. The design intent was usually innocent — suppress the ring that flashed on mouse clicks — but the rule as written also blinds every keyboard.

Who pays is specific. A shopper with a tremor or RSI who cannot use a mouse steers entirely by that ring; without it, checkout becomes guesswork precisely where guessing is most expensive — payment-method radios, the ship-to-a-different-address toggle, the place-order button. A shopper with low vision may have been losing the ring even before the reset, if the theme styled it one pixel wide and pale grey. A switch user gets one input at a time, and the moving indicator is the only confirmation each press did anything. And an invisible focus does not merely slow people down: it causes misfires, where Enter activates the thing the shopper could not see they had reached.

How it is detected here

An honest limit first, because it shapes the method. A focus ring exists only while an element holds focus, so a static scan of the DOM never sees one — and measuring rendered rings by focusing elements in a headless browser proved noisy enough in practice that Klarvo withheld that approach rather than ship false alarms. axe-core carries no rule for 2.4.7 at all; the criterion is a gap most automated scans quietly step around. So Klarvo covers it with its own probe, and is explicit about what that probe reads.

It reads the CSSOM — the parsed stylesheet object model of the rendered page, meaning the author CSS the browser is actually applying, not a guess reconstructed from files on disk. The probe walks every stylesheet the page is permitted to read, descending one level into @media and @supports groups, and counts rules that meet all three conditions: the selector targets :focus or :focus-visible (or applies a bare * reset); the declarations remove the outline — outline: none, outline: 0, a zero width; and the same rule offers no replacement indicator — no box-shadow, no visible border. Cross-origin stylesheets the browser refuses to expose are skipped, not guessed about. Suppression without replacement is the finding, and it is logged as serious.

The three-condition shape is what keeps the check fair: a theme that deliberately swaps the ring for a solid box-shadow has made a design decision, not a failure, and is left alone.

What Klarvo does about it

This family is auto tier for one precise reason: the fix is a constant. It is not generated per site, not adapted per theme, not derived by a model — it is a single pre-audited CSS profile, byte-identical on every store that receives it. That is what makes automatic application honest here: with nothing to guess, the safety case was made once, in advance, rather than re-argued per page.

The profile, verbatim:

:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
	outline: 3px solid #047756 !important;
	outline-offset: 3px !important;
}

Each choice is doing work. :where() keeps the selector at zero specificity, so the rule never outmuscles your theme’s other styling — the two !important declarations are the whole mechanism, and they override exactly the property the reset broke and nothing else. :focus-visible rather than :focus means mouse clicks do not paint rings on buttons; keyboard focus does. The 3-pixel offset lifts the ring off the control so it reads against the page rather than fighting the control’s own fill. And the colour is not arbitrary: the green was contrast-checked before the profile ever shipped, clearing the non-text contrast threshold against both light and near-black backgrounds, so the ring stays legible across a light theme and a dark one.

Delivery is server-side. The profile travels through the plugin’s sanitised inject_css operation — the plugin speaks a small typed language of vetted operations, and inject_css accepts only the pre-audited profiles, so this pathway cannot be bent into carrying arbitrary styles — and is delivered as a stylesheet through WordPress’s own enqueue layer (wp_add_inline_style), prefixed with a verification sentinel (--klarvo-fix-profiles:1) that Klarvo’s re-validation greps in the served source and reads back as a computed style in a real browser. The stylesheet is in the HTML your store serves, which is what a browser, a scanner and an auditor all read. It is never a client-side overlay repainting the page after load.

Then it is re-validated independently, in two halves that match how this fix can fail. The engine re-fetches the page and confirms the block is present in the served source; and a fresh rendered check confirms the computed outline actually survives the rest of your CSS. A stylesheet that still wins the fight fails the re-check honestly, and the item stays open instead of being marked done. An automated fix is recorded with evidence, not assumed.

How to fix it yourself

Find the suppressing rule

Focus something — click into the page, press Tab — and inspect the focused element in DevTools. In the Styles panel, filter for outline: the rule setting it to none or 0 is your culprit, and DevTools names the file it came from. Or search the theme directly: grep -rn 'outline' wp-content/themes/your-theme/ and look for none on focus selectors. Check Appearance → Customise → Additional CSS too — copied resets very often live there.

Delete the reset, or supersede it properly

If the reset is yours, delete it. If it ships inside a parent theme or a plugin stylesheet you should not edit, supersede it with the modern pattern in your child theme:

:focus-visible {
	outline: 2px solid #1f3a5f;
	outline-offset: 2px;
}

:focus:not(:focus-visible) {
	outline: none;
}

This is C15’s approach with current selectors: a visible style when focus needs to be seen, quiet on pointer-driven focus — the legitimate version of what the old reset was reaching for. Choose a colour that holds at least 3:1 against the backgrounds it will sit on, keep the width at 2 pixels or more, and keep the offset.

Block themes: theme.json

From WordPress 6.2, theme.json accepts a custom CSS string, which keeps the fix inside the design system and versioned with the child theme:

{
	"version": 3,
	"styles": {
		"css": ":focus-visible { outline: 2px solid var(--wp--preset--color--contrast); outline-offset: 2px; }"
	}
}

Using a palette token means the ring follows Global Styles if the palette changes — one less hard-coded colour to drift out of contrast.

The WooCommerce spots that get missed

A global rule does not reach every control on its own, because Woo’s surfaces carry styles of their own. Walk these by keyboard rather than assuming: the payment-method radios and the ship-elsewhere checkbox at checkout, quantity steppers, the coupon disclosure link, and the Cart and Checkout blocks’ components (.wc-block-components-button and friends), which ship their own focus styling. And note that a scoped reset is still a reset — a theme that only suppresses .button:focus has hidden the ring exactly where the money is.

If your design uses box-shadow rings

A box-shadow focus ring satisfies 2.4.7 — but pair it with a transparent outline. Windows High Contrast and other forced-colours modes strip shadows and honour outlines, so the transparent outline becomes your indicator there:

.button:focus-visible {
	box-shadow: 0 0 0 3px #1f3a5f;
	outline: 2px solid transparent;
	outline-offset: 2px;
}

How you know it worked

  1. Put the mouse down and Tab through the home page, a product, the basket and checkout. Every stop — link, button, field, radio, checkbox — should show a visible ring. If focus vanishes for one stop, inspect that element while it is focused: some rule is still winning, and DevTools will name it.
  2. Do the click test. Pointer clicks on buttons and links should not flash rings — that is :focus-visible behaving — while a text field should show the ring whenever it is focused.
  3. Re-scan. The probe should now count zero unreplaced suppressions, and where Klarvo applied the profile, its own re-validation has already confirmed the block is served and the outline survives your CSS in a rendered check.
  4. Spot-check the extremes: a dark footer, a hero image, a busy product grid. The offset is what keeps the ring legible there — confirm it visually rather than trusting the theory.
  5. If you have access to Windows, try High Contrast mode; if not, note it for your next human audit — forced-colours behaviour is exactly the sort of thing that lane exists to verify.
  6. Re-check after theme and plugin updates. Any new stylesheet can reintroduce the reset, which is why focus stays monitored rather than fixed once and filed away.

Sources: W3C WAI — Understanding SC 2.4.7: Focus Visible · W3C WAI — Failure F78: styling element outlines and borders in a way that removes or renders non-visible the visual focus indicator · W3C WAI — Technique C15: Using CSS to change the presentation of a user interface component when it receives focus · W3C WAI — Technique G149: Using user interface components that are highlighted by the user agent when they receive focus · MDN — :focus-visible (CSS pseudo-class) · MDN — outline (CSS shorthand property)

FAQ

Questions this raises

Will mouse users suddenly see rings all over my store?

No. The injected profile uses :focus-visible, the selector browsers reserve for focus a person needs to see: keyboard focus paints the ring, while a pointer click on a link or button generally does not. Text fields show it whenever focused — which is exactly what a shopper mid-form wants, since the ring marks where their typing will land.

My theme replaces the outline with a box-shadow ring. Will that be flagged?

No. Detection counts rules that remove the outline without providing a replacement indicator in the same rule — a genuine box-shadow or a visible border counts as one, so a considered custom focus style is left in peace. A background-colour change alone does not count, because it is the pattern F78 describes: a highlight too weak to function as a cursor.

Why has no other scanner ever mentioned this?

Because a focus ring only exists while an element is focused, which a static DOM scan never observes — axe-core ships no rule for 2.4.7 at all. Most automated tools therefore skip the criterion entirely. Klarvo covers it by analysing the stylesheets themselves, and is plain about the method rather than pretending a rendered check happened.

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 19 Klarvo checks, and tells you exactly what it found and where.