
Separating Mouse and Keyboard Focus Rings with focus-visible
One of the most common—and damaging—accessibility regressions on the web is button:focus { outline: none; }, usually added because designers or stakeholders dislike seeing a focus ring linger on a button after clicking it with a mouse. Removing :focus outlines outright leaves keyboard and switch-device users completely blind to where their cursor is positioned on the page.
The :focus-visible pseudo-class solves this dilemma natively. The browser uses heuristics to determine the input modality: when a user navigates via keyboard (Tab, Shift + Tab, or arrow keys) or interacts with a text input, :focus-visible matches and displays a prominent focus indicator. When a user clicks a button with a mouse or taps on a touchscreen, :focus matches but :focus-visible does not.
Example
/* 1. Fallback for older browsers: always show a clear focus ring */
.btn:focus {
outline: 3px solid #04ae92;
outline-offset: 2px;
}
/* 2. Remove the ring ONLY when focused via mouse/touch in supporting browsers */
.btn:focus:not(:focus-visible) {
outline: none;
}
/* 3. Explicit high-contrast ring for keyboard navigation */
.btn:focus-visible {
outline: 3px solid #04ae92;
outline-offset: 2px;
}
Input : Custom interactive buttons, navigation links, and disclosure toggles
Output : Crisp, unmistakable focus rings during Tab keyboard navigation and clean visuals after mouse clicks.
Implementation
<nav class="action-bar" aria-label="Article actions">
<button type="button" class="action-btn">Bookmark Article</button>
<button type="button" class="action-btn">Copy Link</button>
<a href="#comments" class="action-link">Jump to Comments</a>
</nav>
.action-btn,
.action-link {
display: inline-flex;
align-items: center;
padding: 10px 18px;
border: 1px solid #0f172a;
border-radius: 6px;
background-color: #ffffff;
color: #0f172a;
font-size: 15px;
font-weight: 600;
text-decoration: none;
cursor: pointer;
transition: background-color 150ms ease, color 150ms ease;
}
.action-btn:hover,
.action-link:hover {
background-color: #0f172a;
color: #ffffff;
}
/* Progressive enhancement pattern for universal keyboard accessibility */
.action-btn:focus,
.action-link:focus {
outline: 3px solid #04ae92;
outline-offset: 3px;
}
.action-btn:focus:not(:focus-visible),
.action-link:focus:not(:focus-visible) {
outline: none;
}
.action-btn:focus-visible,
.action-link:focus-visible {
outline: 3px solid #04ae92;
outline-offset: 3px;
box-shadow: 0 0 0 6px rgba(4, 174, 146, 0.2);
}
Main Point: Never write outline: none on :focus without pairing it with :focus:not(:focus-visible) and a high-contrast :focus-visible rule so keyboard users always know where focus is located.
Credits
Photo by ales nesetril