Provide visible custom focus indicators
Ensure all interactive elements have a clearly visible focus indicator for keyboard navigation — never just remove the default outline without providing a better alternative.
- Never use outline: none or outline: 0 without a visible :focus-visible replacement
- Use :focus-visible instead of :focus to show indicators only during keyboard navigation
- Focus indicators must have 3:1 contrast ratio against adjacent colors (WCAG 2.2)
- Provide at least 2px solid outline with an offset for a high-quality focus ring
- If a focus ring is drawn with pseudo-elements, verify it is anchored to the correct positioned element and not clipped by `overflow: hidden`
- If the component uses a stretched-link pattern, verify the visible focus ring follows the actual interactive element rather than a parent wrapper by accident
Rule Details
Focus indicators are essential for keyboard navigation. Never remove them without providing a better alternative.
Code Example
/* ❌ Removes focus indicator — keyboard users can't see where they are */
* { outline: none; }
*:focus { outline: none; }
button:focus { outline: 0; }Why It Matters
Users who navigate by keyboard (people with motor disabilities, power users, people in assistive technology contexts) rely entirely on focus indicators to know where they are on the page. Removing outlines without replacement makes an entire site unusable for keyboard users. WCAG 2.4.7 requires a visible focus indicator, and WCAG 2.4.13 defines a stronger size-and-contrast target for custom indicators.
:focus-visible (The Right Approach)
:focus-visible shows focus indicators only during keyboard navigation, not when clicking with a mouse. This satisfies both aesthetics (no ring on click) and accessibility (ring on keyboard).
/* ❌ Old approach: shows ring on mouse click too */
button:focus {
outline: 2px solid blue;
}
/* ✅ Modern approach: only during keyboard navigation */
button:focus { outline: none; } /* Hide default on mouse click */
button:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}A High-Quality Focus Style
/* Base reset for all browsers */
*:focus {
outline: none;
}
/* Visible focus only for keyboard navigation */
*:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 3px;
border-radius: 3px; /* Optional: match element border-radius */
}Component-Specific Focus Rings
/* Inset ring for elements where outline-offset would overlap content */
.button {
position: relative;
}
.button:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Dark surface: use a light ring */
.dark-nav .nav-link:focus-visible {
outline-color: white;
}
/* For inputs: border change instead of outline */
.input:focus-visible {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 3px rgb(37 99 235 / 0.25);
}Skip Links for Keyboard Users
/* Visually hidden but visible on focus */
.skip-link {
position: absolute;
top: -100%;
left: 1rem;
padding: 0.5rem 1rem;
background: var(--color-primary);
color: white;
text-decoration: none;
border-radius: 0 0 0.5rem 0.5rem;
z-index: 1000;
}
.skip-link:focus {
top: 0;
}WCAG 2.2 Requirements
WCAG 2.2 Success Criterion 2.4.11 (Focus Appearance, AA) requires:
- Focus indicator area of at least the perimeter of the unfocused component times 2 CSS pixels
- Contrast ratio of at least 3:1 between focused and unfocused states
- At least 3:1 contrast against adjacent colors
Verification
- Inspect the rendered UI at the breakpoints and interaction states affected by the rule.
- Confirm the computed styles match the intended fix in DevTools.
- Test at least one mobile and one desktop viewport before shipping.
- If the rule affects motion, contrast, or layout stability, verify those user-facing outcomes directly.
Use with AI
Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.
Check
Verify implementation
Find all instances of outline: none, outline: 0, or :focus { outline: none } in this CSS. Check if they provide an alternative visible focus indicator. If the replacement uses pseudo-elements, also verify the ring cannot be clipped or misplaced by overflow or missing positioning context.
Fix
Auto-fix issues
Replace outline: none on :focus with :focus-visible styles that provide a clearly visible custom focus ring with appropriate contrast.
Explain
Learn more
Explain the difference between :focus and :focus-visible, why focus styles matter for accessibility, and how to design an accessible focus ring.
Review
Code review
Review stylesheets, component styles, and responsive states related to Provide visible custom focus indicators. Flag exact selectors, declarations, or breakpoints that violate the rule in the rendered UI.