focus-within vs. focus-visible: The Comparison
AI generated
{ }
@
CSS · Selectors · Accessibility
focus-within vs. focus-visible: The Comparison
Group context versus keyboard aware visibility

focus-within and focus-visible sound similar but solve fundamentally different problems. One reacts to focus somewhere inside a container, the other distinguishes keyboard from mouse interaction. Confusing the two leads either to noisy focus rings or invisible keyboard navigation.

16 min read focus-within · focus-visible · accessibility All modern browsers

1. Why two similar focus pseudoclasses exist

Anyone seeing focus-within and focus-visible side by side for the first time might assume they are two variants of the same concept. In reality, both pseudoclasses solve completely different problems that emerged historically for separate reasons. focus-within answers the question of whether focus is located anywhere inside a container, regardless of which child element holds it. focus-visible, on the other hand, answers whether a visible focus ring should be shown at all, based on the kind of interaction that produced the focus.

This distinction arose because classic :focus had to serve two use cases at once that interfered with each other: developers wanted stylish, grouped focus effects for forms, but at the same time no ugly blue ring on every mouse click on a button. Before focus-visible, teams had to rely on JavaScript libraries such as a focus visible polyfill to distinguish keyboard from mouse interaction. Together, both pseudoclasses now form a complete, native toolset for thoughtful focus design.

2. focus, focus-within and focus-visible in detail

:focus matches exactly the element currently holding keyboard or programmatic focus, regardless of how that focus came about. :focus-within matches an element when it or any descendant holds focus, making it the ideal tool to mark an entire container as soon as one of its children is active. :focus-visible, finally, only matches when the browser decides a visible indicator is appropriate, typically during keyboard navigation, not on a plain mouse click.

The heuristic behind :focus-visible is implemented inside the browser and takes into account, among other things, the input method, the element type, and whether the element is naturally interactive. A button clicked with the mouse typically gets no visible focus ring, while the same button reached via the Tab key does get a ring. A text field, on the other hand, often shows a focus indicator even on mouse click, because the text cursor itself already signals focus, yet users should still know the field is active.


/* :focus matches regardless of interaction method */
button:focus {
  outline: 2px solid #7c3aed;
}

/* :focus-within matches the container when any child has focus */
.form-group:focus-within {
  background: #f5f3ff;
  border-color: #7c3aed;
}

/* :focus-visible matches only when the browser deems it necessary */
button:focus-visible {
  outline: 2px solid #7c3aed;
  outline-offset: 2px;
}

/* Common pattern: remove default outline, restore only when visible */
button:focus:not(:focus-visible) {
  outline: none;
}

3. focus-within in practice: groups and custom components

focus-within proves its value everywhere multiple child elements logically belong together but are individually focusable. A search field with a separate search button is a typical example: instead of highlighting only the input field, the whole surrounding container should be emphasized, regardless of whether the input or the button currently holds focus. .search-box:focus-within { box-shadow: 0 0 0 3px rgba(124,58,237,0.3); } solves this with a single rule, without JavaScript and without treating every child element individually.

A second common area of use is navigation with dropdown menus. A menu item with a submenu should stay open as long as focus is somewhere inside the submenu, for example while a user tabs through the entries. li:focus-within > ul { display: block; } keeps the submenu visible as long as any child element is focused, and closes it automatically once focus leaves the container. This technique works entirely without JavaScript and stays fully keyboard accessible.


/* Highlight the whole search box regardless of which child has focus */
.search-box:focus-within {
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.3);
  border-color: #7c3aed;
}

/* Dropdown stays open while focus is anywhere inside the submenu */
nav li {
  position: relative;
}

nav li > ul {
  display: none;
  position: absolute;
  top: 100%;
}

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

4. focus-visible in practice: keyboard versus mouse

The classic conflict focus-visible solves concerns buttons and links: designers do not want a visible ring on every mouse click, but developers still need a clear indicator for keyboard users. Before focus-visible, the most common, but accessibility violating solution was a global *:focus { outline: none; } that removed the focus ring entirely and locked keyboard users out completely. focus-visible makes this practice unnecessary: button:focus-visible { outline: 2px solid #7c3aed; } shows the ring only when the browser classifies the interaction as keyboard based.

For custom components such as custom checkboxes or cards with a tabindex, it is worth removing the default outline and setting a distinct, clearly visible replacement only under :focus-visible. That way the visual design stays unchanged for mouse interaction, while keyboard users can still tell exactly where focus currently sits. Important: :focus-visible never removes the focus indicator without replacement, it only shifts when it becomes visible.


/* Custom card component with a visible, keyboard-only focus ring */
.card[tabindex="0"] {
  outline: none;
}

.card[tabindex="0"]:focus-visible {
  outline: 3px solid #7c3aed;
  outline-offset: 3px;
  border-radius: 0.5rem;
}

/* Custom checkbox: visible ring only for keyboard interaction */
.checkbox-input:focus-visible + .checkbox-visual {
  box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.4);
}

5. Combining both pseudoclasses for accessible components

The strongest effect comes from combining focus-within and focus-visible. A form field with a label and hint text can highlight the whole block via focus-within, while inside that block focus-visible adds an extra ring on the input only for keyboard use. This produces two-stage feedback: coarse group highlighting for every interaction type, and fine, keyboard specific ring marking for users navigating via Tab.

Another combination pattern applies to complex widgets such as date pickers or comboboxes with several internal elements. .datepicker:focus-within { z-index: 20; } ensures the widget sits above other elements as long as any part of it is focused, while :focus-visible on the individual day buttons handles concrete keyboard navigation inside the calendar. This division of responsibilities, group context versus individual visibility, is the core of a well thought out focus system.

6. Pitfalls: double rings and vanishing outlines

A common mistake is defining a visible outline for both :focus and :focus-visible at once without separating one from the other. The result is double, overlapping rings, or a ring appearing on mouse click, exactly what should have been avoided. The clean solution is the pattern :focus:not(:focus-visible) { outline: none; } followed by a dedicated rule for :focus-visible, so both states never contradict each other.

A second pitfall involves frameworks and utility classes that blanket apply outline: none to all interactive elements without defining a replacement. This removes focus indicators entirely and makes the page unusable for keyboard users, a clear violation of WCAG 2.4.7. Anyone finding outline: none in a global reset should immediately check whether a :focus-visible replacement exists elsewhere before treating the rule as harmless.

7. Browser support and fallback strategies

focus-within has been supported by all current browsers for several years and is considered safe to use, even in projects with conservative browser requirements. focus-visible is also present in all current evergreen browsers, but was only available via a JavaScript polyfill for a long time. For projects that still need to support older browsers, combining native :focus-visible with a fallback via @supports not selector(:focus-visible) makes sense, falling back to a simple, always visible :focus style in older environments.

It is important never to design the fallback so it removes the focus indicator entirely. Better to have one visible ring too many on a mouse click in an old browser than a completely invisible focus for keyboard users. The fallback strategy should therefore lean conservatively toward visibility, not toward invisibility, even if that is not aesthetically perfect.

8. Custom focus styles without breaking accessibility

An individually styled focus indicator still has to meet core accessibility requirements: sufficient contrast against the background, a minimum thickness, and a clearly recognizable difference from the unfocused state. WCAG 2.4.11 requires that the focus indicator not be completely hidden by other content and maintain a minimum contrast of 3:1 against its surroundings. A purely color based difference without an outline or shadow is often not enough, especially for users with limited color vision.

When designing custom focus rings, outline is preferable to border because outline occupies no layout space and does not affect box size. outline-offset adds extra visual distance from the element, which noticeably improves recognizability especially for small, closely spaced interactive elements. Combined with :focus-visible, this allows designing a focus ring that fits the brand without violating accessibility requirements.

9. Comparison table: which pseudoclass when

The following table summarizes which pseudoclass is the right choice for which concrete use case, so the decision can be made quickly and consistently in daily project work.

Use case :focus :focus-within :focus-visible
Highlighting a single input field Suitable Not needed Recommended for rings
Form group with multiple children Does not react Suitable Usable additionally for a ring
Button ring only on keyboard Always shows a ring Not relevant Suitable
Keeping a dropdown open Only on direct focus Suitable Not relevant
Text field with cursor Sufficient Not needed Usually visible anyway

As a rule of thumb: use focus-within for groups and containers, focus-visible for individual interactive elements where mouse and keyboard interaction should be treated differently. Both can be used together in a single component without interfering with each other.

Mironsoft

Accessible frontends, modern CSS architecture and Hyvä themes

Focus management that keyboard users can actually see?

We audit existing components for WCAG compliant focus indicators and deploy focus-within and focus-visible deliberately for accessible, well operable interfaces.

Focus Audit

Checking keyboard navigation and focus indicators against WCAG 2.4.7 and 2.4.11

Component Refactoring

Integrating focus-within and focus-visible into existing custom components

Design System

Consistent, on brand focus rings for every interactive element

10. Summary

focus-within and focus-visible solve different problems and complement rather than replace each other. focus-within marks a container as soon as any child element holds focus, ideal for form groups, search fields and dropdown menus. focus-visible decides whether a focus ring appears appropriate to the input method, resolving the historical conflict between clean mouse design and accessible keyboard navigation.

Anyone who deliberately combines both pseudoclasses can build components that look visually tidy while remaining fully accessible via keyboard. Blanket removal of focus indicators with outline: none and no replacement remains a mistake in every case, regardless of how well the rest of the component is designed. The patterns shown here can be applied directly to existing forms, navigations and custom components.

focus-within vs. focus-visible: The Key Points at a Glance

focus-within

Marks a container as soon as any descendant holds focus. Ideal for groups, search fields and dropdowns.

focus-visible

Shows the focus ring only when the browser classifies the interaction as keyboard based. Resolves the mouse versus keyboard conflict.

Combination

focus-within for the coarse container, focus-visible for the fine ring on the element, both usable together.

Accessibility

outline: none without replacement is always a mistake. Minimum contrast of 3:1 and clear visibility are mandatory.

11. FAQ: focus-within vs. focus-visible

1Main difference focus-within vs. focus-visible?
focus-within marks containers on child focus, focus-visible decides by interaction method whether a ring appears.
2Can both be used together?
Yes, recommended: focus-within for the group, focus-visible for the fine ring on the individual element.
3Why avoid outline: none?
Without a replacement it makes the page unusable for keyboard users, a violation of WCAG 2.4.7.
4Ring on text fields even with a mouse?
Often yes, because browsers consider a visible focus useful there even on mouse click.
5Keeping a dropdown open on focus?
li:focus-within > ul { display: block; } keeps the menu open as long as focus is anywhere inside.
6What does focus:not(:focus-visible) do?
Removes the default ring on non keyboard interaction and prevents contradictory focus styles.
7Is focus-within supported everywhere?
Yes, for years in all current browsers, considered safe to use.
8What contrast is mandatory?
At least 3:1 per WCAG 2.4.11, plus sufficient visibility without being hidden.
9Why outline instead of border?
outline takes no layout space, border shifts surrounding elements and causes jumps.
10Fallback without focus-visible?
@supports not selector(:focus-visible) provides an always visible :focus style as a safe alternative.