Understanding CSS Focus States: :focus
vs :focus-visible
Focus states in CSS help make websites accessible and user-friendly, especially for keyboard users and those using assistive technologies. The :focus
and :focus-visible
pseudo-classes allow developers to style elements when they are active, improving usability and accessibility.
A focus state occurs when an interactive element—like a button, link, or input—is active, usually through Tab key navigation or a mouse click.
CSS provides two main pseudo-classes for focus states: :focus
and :focus-visible
.
:focus
vs :focus-visible
:focus
Pseudo-Class
The :focus
pseudo-class styles an element whenever it gets focus, whether by mouse, keyboard, touch, or JavaScript. It’s commonly used for buttons, inputs, and links. Because it applies to all focus events, it can sometimes look distracting for mouse users. Always keep a clear focus style, rather than removing it, to ensure accessibility.
Example:
button:focus {
outline: 3px solid #007BFF; /* Universal focus indicator */
outline-offset: 2px;
}
:focus-visible
Pseudo-Class
The :focus-visible
pseudo-class styles an element only when it’s meaningful, usually for keyboard navigation or assistive technology users. Mouse clicks typically don’t trigger it, keeping the interface cleaner. It ensures keyboard users see a clear focus indicator without cluttering the UI for mouse users.
Example:
button:focus-visible {
outline: 3px solid #FF6200; /* Keyboard focus indicator */
outline-offset: 3px;
box-shadow: 0 0 5px rgba(255, 98, 0, 0.5);
}
Key Differences:
Feature | :focus | :focus-visible |
---|---|---|
Trigger | All focus events (mouse, keyboard, touch, script) | Primarily keyboard or assistive tech focus |
Use Case | General focus styling | Keyboard-specific, accessibility-first styling |
Visual Impact | Can be distracting on mouse clicks | Reduces clutter, appears only when needed |
Browser Support | Universal | Modern browsers (2020+) |
Accessibility Risk | High if default styles removed | Low; respects browser heuristics |
Example 1: Accessible Login Form
<a href="#main-content" class="skip-link">Skip to Main Content</a>
<form>
<div class="form-group">
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" placeholder="Enter password">
</div>
<button type="submit">Log In</button>
</form>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #1a73e8;
color: white;
padding: 8px;
z-index: 100;
}
.skip-link:focus { top: 0; }
input, button {
padding: 10px;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
width: 100%;
box-sizing: border-box;
}
/* Focus Styles */
input:focus, button:focus {
outline: 2px solid #1a73e8; /* Fallback */
}
input:focus-visible, button:focus-visible {
outline: 3px solid #ff6200;
outline-offset: 3px;
box-shadow: 0 0 5px rgba(255, 98, 0, 0.5);
}
button { background: #1a73e8; color: white; border: none; cursor: pointer; }
button:hover { background: #1557b0; }
Example 2: Keyboard-Friendly Navigation Menu
<nav>
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#services">Services</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
nav { background: #333; padding: 10px; }
ul { list-style: none; display: flex; gap: 15px; margin: 0; padding: 0; }
li a { color: white; text-decoration: none; padding: 10px 15px; border-radius: 4px; display: block; }
li a:hover { background: #555; }
li a:focus { outline: 2px solid #fff; outline-offset: 2px; }
li a:focus-visible { outline: 3px solid #ffeb3b; outline-offset: 3px; background: #444; }