CSS :focus-visible and :focus-within: Accessible Focus Styling
This article explains how the CSS :focus-visible and :focus-within pseudo-classes work, when to use each one, and how they help you build keyboard-friendly, accessible interfaces without over-styling mouse users.
Quick answer: Use :focus-visible when you want a focus style to appear mainly for keyboard-like focus, and use :focus-within when a parent element should react because one of its descendants is focused.
Difficulty: Beginner
You'll understand this better if you know: basic CSS selectors, how focus works in forms and links, and the difference between element states like hover and active.
1. What Are :focus-visible and :focus-within?
The :focus-visible and :focus-within pseudo-classes let CSS respond to focus in more precise ways than plain :focus.
- :focus-visible matches an element when the browser decides the focus indicator should be shown.
- :focus-within matches a parent element when it or any of its descendants has focus.
- Both help make focus styling more useful and less visually noisy.
They are especially important for accessibility because keyboard users need a clear visual indicator of where focus is on the page.
2. Why They Matter
Focus styling is one of the easiest ways to improve keyboard accessibility. If links, buttons, and inputs do not show a clear focus state, keyboard users can get lost while tabbing through the page.
:focus-visible helps you avoid showing focus rings in situations where they are less helpful, such as after a mouse click in many browsers. :focus-within helps you style containers like form fields, menus, or cards when a child element receives focus.
That means you can design clearer interfaces without removing the browser's built-in accessibility behavior.
3. Basic Syntax or Core Idea
Using :focus-visible
The simplest pattern is to style elements when the browser considers the focus to be visibly relevant.
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}This rule adds a clear outline only when the browser thinks a visible focus indicator is appropriate.
Using :focus-within
The :focus-within pseudo-class is often used on a container, not the focused element itself.
.field:focus-within {
border-color: #005fcc;
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.2);
}This rule changes the appearance of a wrapper whenever anything inside it is focused.
How they differ from :focus
:focus matches every focused element, no matter how focus happened. That is useful, but it can be too broad if you want a more polished focus treatment.
:focus-visible is more selective. :focus-within is about a focused descendant causing a parent to match.
4. Step-by-Step Examples
Example 1: Button focus ring with :focus-visible
This example styles a button so the focus ring is clear for keyboard users while avoiding unnecessary emphasis in other cases.
button {
padding: 0.75rem 1rem;
border: 1px solid #888;
border-radius: 0.5rem;
}
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 3px;
}The button always keeps its normal appearance, but keyboard focus gets a strong and visible ring.
Example 2: Input group highlighting with :focus-within
This example makes a form field wrapper react when the text input inside it is focused.
.field {
padding: 0.75rem;
border: 1px solid #bbb;
border-radius: 0.5rem;
}
.field:focus-within {
border-color: #005fcc;
background: #f7fbff;
}When the input is focused, the container shows the active state, which helps users understand which group they are editing.
Example 3: Navigation item styling
Here, :focus-visible gives keyboard users a clear indicator on links in a menu.
nav a {
display: inline-block;
padding: 0.5rem 0.75rem;
text-decoration: none;
}
nav a:focus-visible {
outline: 2px solid currentColor;
outline-offset: 4px;
}This keeps the menu clean while still meeting accessibility needs.
Example 4: Card with a button inside
:focus-within is useful when the visual container should react to keyboard focus inside it.
.card {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 0.75rem;
}
.card:focus-within {
border-color: #005fcc;
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.15);
}The card now feels active when focus moves to the button inside it, not just when the card itself is clicked.
5. Practical Use Cases
- Highlighting buttons, links, and custom controls for keyboard users.
- Styling form groups when an input, select, textarea, or checkbox inside them receives focus.
- Showing active states in search bars, login forms, and settings panels.
- Making sidebar menus and navigation items easier to scan during tab navigation.
- Creating accessible composite widgets where the outer container should reflect inner focus.
6. Common Mistakes
Mistake 1: Removing focus outlines without replacing them
Many beginners hide outlines because they look visually strong, but that can make keyboard navigation hard or impossible to follow.
Problem: This removes the browser's focus indicator and leaves keyboard users with no clear visual cue.
button:focus {
outline: none;
}Fix: Replace the default outline with a custom focus style, ideally using :focus-visible.
button:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}The corrected version keeps focus visible while giving you control over the design.
Mistake 2: Expecting :focus-within to style the child element automatically
:focus-within matches the parent container, not the focused input itself.
Problem: This rule will not style the input directly, because the selector applies to .field instead of the nested control.
.field:focus-within input {
border-color: #005fcc;
}Fix: Style the input itself with :focus or :focus-visible, and use :focus-within on the wrapper.
.field:focus-within {
border-color: #005fcc;
}
.field input:focus-visible {
outline: none;
}This works because each selector targets the element it is meant to affect.
Mistake 3: Using :focus-visible for effects that should always appear
Not every focus style should depend on the browser's visibility heuristics.
Problem: If you need a state to appear whenever an element has focus, :focus-visible can be too narrow and may not trigger in the cases you expect.
.search:focus-visible {
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.2);
}Fix: Use :focus when the visual change must appear for any focused state, or combine both selectors thoughtfully.
.search:focus {
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.2);
}The corrected version ensures the focus state is visible whenever the control is focused.
7. Best Practices
1. Prefer :focus-visible for interactive controls
For buttons, links, and other keyboard-operated controls, :focus-visible usually gives the cleanest result because it preserves a strong indicator where it matters most.
a:focus-visible,
button:focus-visible {
outline: 2px solid currentColor;
outline-offset: 3px;
}This keeps the interface accessible without overemphasizing every mouse click.
2. Use :focus-within for grouped UI components
When a control is visually part of a larger unit, such as a search bar or a form field wrapper, :focus-within makes the container behave like part of the focus state.
.search-box:focus-within {
border-color: #005fcc;
background-color: #fff;
}This helps users see the active region, not just the caret inside it.
3. Keep focus styles visible and high contrast
Focus styles should stand out against surrounding content. A subtle color change is often not enough, especially for users with low vision or on busy backgrounds.
input:focus-visible {
outline: 3px solid #1d4ed8;
outline-offset: 2px;
}Strong contrast makes the focus location easier to recognize immediately.
8. Limitations and Edge Cases
- :focus-visible support is good in modern browsers, but older browsers may behave differently or fall back to :focus behavior.
- Keyboard, mouse, touch, and script-driven focus can produce different visibility results depending on browser heuristics.
- :focus-within can match unexpectedly deep descendants, which is useful in forms but can be surprising in complex layouts.
- Custom controls still need a real focusable element; CSS cannot make a non-focusable element receive keyboard focus by itself.
- Replacing outlines with box shadows can work well, but shadows may be clipped by overflow or blend poorly with high-contrast themes.
9. Practical Mini Project
Let's build a small login panel that uses both pseudo-classes together: the button and inputs get clear focus treatment, and the outer card highlights when any field inside it is active.
<form class="login-card">
<div class="field">
<label for="email">Email</label>
<input id="email" type="email">
</div>
<div class="field">
<label for="password">Password</label>
<input id="password" type="password">
</div>
<button type="submit">Sign in</button>
</form>:focus-within works on the card and field wrappers, while :focus-visible works on the actual form controls.
.login-card {
max-width: 24rem;
padding: 1.25rem;
border: 1px solid #d1d5db;
border-radius: 0.75rem;
}
.login-card:focus-within {
border-color: #005fcc;
}
.field {
margin-bottom: 1rem;
}
.field:focus-within label {
color: #005fcc;
}
input {
width: 100%;
padding: 0.7rem;
border: 1px solid #9ca3af;
border-radius: 0.5rem;
}
input:focus-visible {
outline: 3px solid #005fcc;
outline-offset: 2px;
}
button {
padding: 0.75rem 1rem;
border: 0;
border-radius: 0.5rem;
background: #005fcc;
color: white;
}
button:focus-visible {
outline: 3px solid #111827;
outline-offset: 3px;
}This small pattern scales well because each selector handles one responsibility: container focus, field grouping, and control focus.
10. Key Points
- :focus-visible is for styling elements when the browser shows a visible focus indicator.
- :focus-within is for styling a parent when any of its descendants receives focus.
- Use :focus-visible for interactive controls and :focus-within for grouped components.
- Never remove focus styles without replacing them with an accessible alternative.
- Good focus styling helps keyboard users navigate confidently.
11. Practice Exercise
Create a contact form with three fields and a submit button. Style each focused input with :focus-visible and make each field wrapper highlight with :focus-within.
- Each field should have a label, input, and wrapper.
- The wrapper should change border color when the input is focused.
- The input itself should show a visible outline when focused.
Expected output: Tabbing through the form clearly shows which control is focused, and the active field group is easy to identify.
Hint: Use one selector for the wrapper and another selector for the actual input.
.contact-form {
max-width: 32rem;
}
.field {
margin-bottom: 1rem;
padding: 0.75rem;
border: 1px solid #cbd5e1;
border-radius: 0.5rem;
}
.field:focus-within {
border-color: #2563eb;
background-color: #eff6ff;
}
input {
width: 100%;
padding: 0.65rem;
border: 1px solid #94a3b8;
border-radius: 0.375rem;
}
input:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 2px;
}
button {
padding: 0.75rem 1rem;
border: 0;
border-radius: 0.5rem;
background: #2563eb;
color: white;
}12. Final Summary
:focus-visible and :focus-within solve different but related accessibility problems. :focus-visible helps you show a useful focus indicator on the element that received focus, while :focus-within lets a parent element reflect focus inside its subtree.
Used together, they make interfaces easier to navigate with the keyboard and easier to understand visually. They also help you avoid the common mistake of either hiding focus completely or applying focus styles too broadly.
As a next step, review the focus styles in your own forms, menus, and buttons, then replace any removed outlines with clear :focus-visible rules and add :focus-within to containers that should react as a group.