CSS:focus-visible & :focus-within

:focus-visible & :focus-within

Plain :focus matches any time an element has focus, regardless of how that focus was triggered — clicking with a mouse, tapping on a touchscreen, or navigating with the keyboard all count. Two more targeted pseudo-classes, :focus-visible and :focus-within, solve problems that plain :focus can't: showing a focus ring only when it is actually useful, and reacting to focus anywhere inside a container.

The old anti-pattern
outline: none is an accessibility hazard
For a long time, a very common (and very harmful) pattern was removing the default focus outline globally because "it looks ugly when I click a button":

CSS
/* Please don't do this */
:focus {
  outline: none;
}

This removes the focus indicator for EVERYONE, including keyboard users who rely entirely on that visible ring to know where they are on the page. Without it, tabbing through a form or a menu becomes effectively unusable for anyone not using a mouse.

:focus-visible — the real fix

:focus-visible matches only when the browser's own heuristic decides a focus indicator should be shown — typically keyboard navigation (Tab), and typically NOT a mouse click on a button (where the browser assumes a visible ring isn't necessary). This lets you keep default browser styling for click-focus while still styling a clear ring for keyboard focus.

CSS
/* Remove the ring only where the browser decides it isn't needed */
.button:focus {
  outline: none;
}

/* But always show a clear, custom ring for keyboard focus */
.button:focus-visible {
  outline: 2px solid #2563eb;
  outline-offset: 2px;
}

A mouse click on the button leaves it focused but without an intrusive ring; tabbing to the same button with a keyboard shows the outline clearly. This is the modern, accessible replacement for blanket outline: none.

:focus-within — style a parent when a child has focus

:focus-within matches an element if that element, OR any of its descendants, currently has focus. It's the pseudo-class equivalent of "highlight this whole container while the user is working inside it."

CSS
.form-group {
  padding: 0.75rem;
  border: 1px solid #ddd;
  border-radius: 6px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}

/* Highlight the entire group when its input is focused,
   not just the input itself */
.form-group:focus-within {
  border-color: #2563eb;
  box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15);
}

HTML
<div class="form-group">
  <label for="username">Username</label>
  <input id="username" type="text" />
</div>
Where each one fits
  • :focus — the element itself has focus, no matter how it got it.

  • :focus-visible — the element has focus AND the browser thinks a visible indicator is warranted.

  • :focus-within — the element or ANY descendant has focus (styling a container, not the focused element itself).

Note
This is only an introduction. For a fuller treatment of building accessible, robust focus indicators across an entire site — contrast requirements, `outline` vs `box-shadow`, handling custom components, and more — see the dedicated Focus Styles page.
Rule of thumb
Reach for `:focus-visible` on interactive elements you style yourself (buttons, custom controls), and `:focus-within` whenever you want a wrapper, card, or form group to react to focus happening somewhere inside it.