Articles

CSS :focus-within Explained

The :focus-within pseudo-class styles a parent element when any descendant has focus — perfect for highlighting a form field's container.

Takina Takina · · 4 min read
Close-up of CSS code on a screen

The :focus-within pseudo-class matches an element if it, or any of its descendants, currently has focus. It solves a problem plain :focus can’t: styling a container — a search bar, a form group, a card — based on whether something inside it is focused, without any JavaScript.

The problem it solves

:focus only matches the element that is actually focused. If a user tabs into an <input> nested inside a <div class="search-bar">, :focus can style the input, but it can’t reach up and style the wrapping div. Historically, doing that required a focus event listener that toggled a class on the parent — a small but real chunk of JavaScript for a purely visual effect.

:focus-within moves that logic into CSS:

.search-bar {
  border: 1px solid #ccc;
  transition: border-color 0.15s ease;
}

.search-bar:focus-within {
  border-color: #2563eb;
  box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.2);
}

Now the whole search bar lights up when its <input> is focused — no listener, no state, no cleanup.

How it matches

:focus-within matches an element when:

  • The element itself is focused, or
  • Any element it contains (at any depth) is focused.

That “any depth” part matters. A <form> with a dozen nested <div>s will still match :focus-within when a deeply buried <input> receives focus. This makes it well suited to component wrappers where you don’t control — or don’t want to hardcode — how deep the focusable element sits in the markup.

:focus-within vs :focus vs :focus-visible

These three pseudo-classes look similar but answer different questions:

Pseudo-classMatchesTypical use
:focusThe element itself, when focusedStyling the focused control directly
:focus-visibleThe element itself, only when the browser thinks focus should be visibly indicated (e.g. keyboard nav)Focus rings that don’t appear on mouse clicks
:focus-withinThe element or any descendant that is focusedHighlighting a container around a focused child

For a deeper look at the difference between the first two, see :focus-visible vs :focus. You can combine all three: a card might use :focus-within to highlight itself while an inner button uses :focus-visible to avoid a focus ring on click.

Practical patterns

Highlighting a fieldset. Group related inputs and show which group is active:

fieldset:focus-within {
  background: #f8fafc;
}

Revealing hidden UI on focus. A common accessible pattern is a “skip to content” link or a toolbar that’s visually hidden until something inside it is reachable by keyboard:

.toolbar {
  opacity: 0;
  pointer-events: none;
}

.toolbar:focus-within {
  opacity: 1;
  pointer-events: auto;
}

This is more robust than :hover-only reveal patterns, which strand keyboard users entirely — tabbing into the toolbar’s buttons now reliably shows it. This pairs well with the broader guidance in web accessibility basics: don’t rely on hover for anything a keyboard or screen-reader user needs to reach.

Dropdown menus without JavaScript. A parent <li> can reveal a nested <ul> when a link inside it is focused, giving keyboard users the same reveal behavior mouse users get from :hover:

li ul {
  display: none;
}

li:hover ul,
li:focus-within ul {
  display: block;
}

Selector combinations

:focus-within composes with other selectors the same way any pseudo-class does. It works well with :has() for more conditional styling — for example, styling a form label differently depending on whether its associated input is both focused and invalid. It also combines naturally with structural selectors like :nth-child() when you need to target specific rows in a focused table or list.

Browser support and gotchas

:focus-within has been broadly supported across modern browsers for years and needs no fallback for current evergreen browsers. A couple of things to watch for:

  • It does not fire for elements that are focusable only in principle (e.g. a <div> without tabindex) — the descendant actually has to receive focus.
  • Removing an element from the DOM while it’s focused (or its focused descendant) removes the :focus-within match immediately, since there’s nothing left to hold focus.
  • It has no effect on elements that can never contain focusable descendants, like a plain <span> with only text — there’s nothing inside for focus to move into.

The takeaway

:focus-within lets a container react to focus landing anywhere inside it, replacing a common class of JavaScript focus-tracking with a single CSS selector. Reach for :focus when you’re styling the focused element itself, :focus-visible when you want to suppress focus rings on mouse interaction, and :focus-within when a parent needs to respond to focus happening somewhere inside it.

Takina Takina · · 4 min read

CSS field-sizing Property Explained

The CSS field-sizing property lets form controls like textareas grow to fit their content automatically, without JavaScript resize listeners.

#CSS #Web Development #Frontend