:focus-within matches an element when that element, or any element inside it, has focus. So you point the selector at a container, put a regular input or link inside, and the container restyles itself the moment focus lands anywhere within it. That is the whole feature, and it is the cleanest way to highlight a form field, a card, or a menu while a child is active:
/* The whole field group lights up when its input is focused */
.field:focus-within {
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.25);
}<label class="field">
<span>Email</span>
<input type="email" name="email">
</label>No JavaScript, no focus/blur listeners toggling a class. The browser tracks focus for you and the parent reacts. It is safe to use everywhere: :focus-within has been Baseline since January 2020 (Chrome, Edge, Firefox, and Safari all support it, prefix-free). The only engine that never shipped it is Internet Explorer, which Microsoft retired in June 2022, so that caveat is gone.
Why this is not just :focus
:focus matches only the focused element itself. If you style .field:focus, the input has to be the thing you targeted, and the surrounding <label>, border, and helper text below it cannot react. There is no parent selector in plain :focus. That was the reason this used to need JavaScript: you would listen for focus and blur on the input and add a class to the wrapper.
:focus-within collapses all of that into one selector. It matches the ancestor when focus is on any descendant, which is exactly the "style the container, not the control" behaviour forms want.
Highlight a whole form group
The most common use is a labelled field where the label, the input, and the validation hint should all respond together. Wrap them in one element and key off :focus-within:
.field {
display: grid;
gap: 0.25rem;
padding: 0.75rem;
border: 1px solid #d1d5db;
border-radius: 8px;
transition: border-color 0.15s, box-shadow 0.15s;
}
.field:focus-within {
border-color: #2563eb;
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.25);
}
.field:focus-within > span {
color: #2563eb; /* the label colour shifts too */
}The transition lives on the base .field rule, not the :focus-within one, so the highlight animates in and out. This same shape works for a search bar (style the rounded container, not the bare input), a card that holds a link, or a comment box with a toolbar.
Keep a dropdown open while a child is focused
A menu that only opens on :hover is unusable with a keyboard, because there is no hover when you are tabbing. :focus-within fixes this: keep the panel open as long as focus is somewhere inside it, which is true while a keyboard user tabs through its links.
.menu__panel {
display: none;
}
/* Open on pointer hover, OR while any child is focused */
.menu:hover .menu__panel,
.menu:focus-within .menu__panel {
display: block;
}Split the :hover and :focus-within selectors with a comma into two rules in the list (as above) rather than chaining them, and keep them as separate entries. Historically that comma was load-bearing for a different reason (old Internet Explorer would drop an entire selector list if it did not understand one entry in it), which is why you still see the pattern written this way. It no longer matters for support, but separate, readable rules are still the right call.
This is the accessible replacement for the old hover-only flyout menu. Pair it with :focus-visible on the menu's links so the focus ring shows for keyboard users without flashing for mouse clicks; see CSS :focus-visible for that distinction.
Combine it with :has() for the inverse
:focus-within always styles the ancestor when a descendant is focused. When you need something more specific (style a sibling, or style the parent only when a particular kind of child is focused), reach for :has(), the general parent selector that :focus-within predates.
/* Equivalent to :focus-within, written with :has() */
.field:has(:focus) { border-color: #2563eb; }
/* More specific: react only when the email input is focused */
.field:has(input[type="email"]:focus) { border-color: #16a34a; }
/* Style a sibling label when the checkbox is focused */
.option:has(input:focus) + .option__hint { opacity: 1; }.field:has(:focus) does the same job as .field:focus-within, so when you only want "any descendant focused," keep :focus-within: it is shorter, reads clearly, and has slightly broader history. Reach for :has() when the condition is narrower than "anything inside is focused." The two are complementary, not competing. For the full guide to the parent selector and what it can and cannot nest, see the CSS :has() parent selector.
A note on accessibility
:focus-within is a styling hook, not a substitute for proper focus management. It does not move focus, trap it, or make a non-interactive element focusable. It only reacts to focus that the document already has. So keep using real interactive elements (<input>, <a>, <button>) inside the container; do not bolt tabindex onto a <div> just to make :focus-within fire. And remember the highlight is purely visual: it must not be the only signal of state for anything that matters, since the focus ring on the actual control is what most assistive tech announces.
FAQ
See also
- CSS :focus-visible: keep focus rings for keyboard users and hide them for mouse clicks, the natural pairing for any focusable container.
- The CSS :has() parent selector: the general parent selector that
:focus-withinis a narrower, older case of. - CSS accent-color: tint native checkboxes, radios, and range sliders to match the focus styling on your form groups.
Sources
Authoritative references this article was fact-checked against.
- :focus-within (MDN Web Docs)developer.mozilla.org
- :focus-within browser support (Can I use)caniuse.com
- :has() (MDN Web Docs)developer.mozilla.org





