Home / Snippets / Accessibility /

focus-visible focus ring

Show focus rings for keyboard users, hide them for mouse clicks.

Link item

Try clicking vs. pressing Tab to see the difference.

Widely supported
a11yui

Quick implementation

/* Global focus ring — apply to all interactive elements */
:focus-visible {
  outline: 2px solid oklch(0.52 0.22 265);
  outline-offset: 3px;
}

/* Remove ring on mouse/touch click */
:focus:not(:focus-visible) {
  outline: none;
}

/* Dark mode: brighter ring for contrast */
@media (prefers-color-scheme: dark) {
  :focus-visible {
    outline-color: oklch(0.7 0.2 265);
  }
}

/* High contrast: thicker ring */
@media (forced-colors: active) {
  :focus-visible {
    outline: 3px solid LinkText;
  }
}

Prompt this to your LLM

Includes role, constraints, and edge cases.

You are a senior frontend engineer focused on accessibility.

Goal: A global focus ring system that only shows for keyboard navigation.

Technical constraints:
- Use :focus-visible for keyboard-triggered focus.
- Use :focus:not(:focus-visible) { outline: none } to hide ring on mouse click.
- Outline: 2px solid accent color, 3px offset.
- Use oklch() for the outline color.
- Include @media (prefers-color-scheme: dark) with a brighter outline color.
- Include @media (forced-colors: active) for Windows High Contrast mode — use system colors.
- The outline must NEVER be removed without a replacement — WCAG 2.4.7 requires visible focus.
- outline-offset creates breathing room between the element and the ring.

Apply globally via :focus-visible on :root or *, and override per-component if needed.

Return CSS only.

Why this matters

Focus rings are the #1 accessibility requirement designers want to remove — and the #1 thing keyboard users need to navigate. :focus-visible solves the conflict: the ring shows only when the browser detects keyboard navigation (Tab key), not mouse clicks. Both groups are happy.

The logic

:focus-visible matches when the browser determines focus should be visible — typically after keyboard interaction. :focus:not(:focus-visible) matches mouse/touch focus and removes the outline. The outline-offset: 3px creates visual space between the element and ring, making it look intentional rather than like a browser default.

Accessibility & performance

WCAG 2.4.7 (Focus Visible) requires a visible focus indicator. Never use outline: none without :focus-visible — that removes focus for keyboard users. In forced-colors mode, use system color keywords (LinkText, Highlight) since custom colors are overridden. Zero performance cost — outlines are painted on the compositor layer.