No visible focus indicator measured
focus/indicator-missing
- WCAG success criterion
- 2.4.7 (level AA)
- Outcome class
- Hint only. An automated check that by design reports review hints only: its findings are marked for manual review, never as confirmed violations.
- Area
- Accessibility, Navigation & Operation
- Default severity
- high
- axe-core rule
focus-visible
Why we flag this
Keyboard users cannot see which element currently has focus.
Focus pass: the element's computed styles before and after focusing are equal, as far as they can serve as an indicator (outline, box-shadow, border, background, color, text-decoration).
What the rule checks
Scope. Rendered, keyboard-focusable controls of the top document and its open shadow roots (at most 300 per page).
Expectation. needs_review once per control whose computed style does not change when it receives focus in a way that can serve as an indicator (Layout::focus_visible, measured by accessibility::probes): no new or changed outline, box-shadow, border, background, text colour or text decoration, and no browser focus ring. The browser's own focus ring, an author outline, a background change and a shadow each count as an indicator. A control that cannot take focus, or that is not reachable with Tab (tabindex=-1), is not a candidate. Transitions are suspended during the measurement so a transition on the focus style does not hide the change. Review, not violation: whether the change is perceivable is a human judgement. A page whose scripts break the probe records the rule as not run (CapabilityMissing).
Source. WCAG 2.2 SC 2.4.7 Focus Visible
Detection: shared; needs dom, computed_style, interaction.
Failing example
The check flags this for review. Corpus case interaction_focus_indicator, element button#fi-bare. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Controls with and without a visible focus indicator</title>
<style>
body { font-family: sans-serif; color: #111; background: #fff; margin: 0; padding: 16px; }
a, button, input { display: inline-block; min-width: 44px; min-height: 44px; padding: 8px 12px; box-sizing: border-box; margin: 4px; }
/* Replaces the browser's focus ring with nothing: focusing changes no style. */
.skip { position: absolute; left: -9999px; }
.skip:focus { left: 16px; top: 16px; background: #fff; }
.bare { outline: none; border: 1px solid #767676; background: #fff; color: #111; }
/* An author ring on focus. */
.ring:focus-visible { outline: 3px solid #0050a0; outline-offset: 2px; }
/* No ring, but the background changes: still an indicator. */
.fill { outline: none; background: #fff; }
.fill:focus { background: #ffe58a; }
/* No ring, but a shadow appears. */
.glow { outline: none; }
.glow:focus { box-shadow: 0 0 0 3px #0050a0; }
</style>
</head>
<body>
<a class="skip" href="#main">Skip to main content</a>
<header><p>Garden shop</p>
<nav aria-label="Main"><a href="/" aria-current="page">Home</a> <a href="/shop">Shop</a></nav></header>
<main id="main">
<h1>Focus indicators</h1>
<!-- needs_review: the focus ring is removed and nothing replaces it -->
<button id="fi-bare" class="bare" type="button">Bare button</button>
<a id="fi-bare-link" class="bare" href="/cart">Bare link</a>
<input id="fi-bare-input" class="bare" type="text" aria-label="Bare field">
<!-- absent: the browser's own focus ring -->
<button id="fi-default" type="button">Default ring</button>
<a id="fi-default-link" href="/shop">Default link</a>
<!-- absent: author-drawn indicators -->
<button id="fi-ring" class="ring" type="button">Author ring</button>
<button id="fi-fill" class="fill" type="button">Fill change</button>
<button id="fi-glow" class="glow" type="button">Shadow</button>
<!-- absent: not reachable with Tab, so not a candidate -->
<button id="fi-skipped" class="bare" type="button" tabindex="-1">Skipped</button>
</main>
<footer><p>Garden shop, 2026</p></footer>
</body>
</html>Passing example
The check reports nothing here. Corpus case interaction_focus_indicator, element button#fi-default. The page may contain further findings for other rules.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Controls with and without a visible focus indicator</title>
<style>
body { font-family: sans-serif; color: #111; background: #fff; margin: 0; padding: 16px; }
a, button, input { display: inline-block; min-width: 44px; min-height: 44px; padding: 8px 12px; box-sizing: border-box; margin: 4px; }
/* Replaces the browser's focus ring with nothing: focusing changes no style. */
.skip { position: absolute; left: -9999px; }
.skip:focus { left: 16px; top: 16px; background: #fff; }
.bare { outline: none; border: 1px solid #767676; background: #fff; color: #111; }
/* An author ring on focus. */
.ring:focus-visible { outline: 3px solid #0050a0; outline-offset: 2px; }
/* No ring, but the background changes: still an indicator. */
.fill { outline: none; background: #fff; }
.fill:focus { background: #ffe58a; }
/* No ring, but a shadow appears. */
.glow { outline: none; }
.glow:focus { box-shadow: 0 0 0 3px #0050a0; }
</style>
</head>
<body>
<a class="skip" href="#main">Skip to main content</a>
<header><p>Garden shop</p>
<nav aria-label="Main"><a href="/" aria-current="page">Home</a> <a href="/shop">Shop</a></nav></header>
<main id="main">
<h1>Focus indicators</h1>
<!-- needs_review: the focus ring is removed and nothing replaces it -->
<button id="fi-bare" class="bare" type="button">Bare button</button>
<a id="fi-bare-link" class="bare" href="/cart">Bare link</a>
<input id="fi-bare-input" class="bare" type="text" aria-label="Bare field">
<!-- absent: the browser's own focus ring -->
<button id="fi-default" type="button">Default ring</button>
<a id="fi-default-link" href="/shop">Default link</a>
<!-- absent: author-drawn indicators -->
<button id="fi-ring" class="ring" type="button">Author ring</button>
<button id="fi-fill" class="fill" type="button">Fill change</button>
<button id="fi-glow" class="glow" type="button">Shadow</button>
<!-- absent: not reachable with Tab, so not a candidate -->
<button id="fi-skipped" class="bare" type="button" tabindex="-1">Skipped</button>
</main>
<footer><p>Garden shop, 2026</p></footer>
</body>
</html>This page is generated from the rule catalogue and the rule contracts; see therule overview for how the outcome classes are derived.