It's long been common practice to remove outline via CSS outline: none;
from interactable elements such as buttons, since most of the time it looks off brand, but it hinders accessibility and makes navigating websites harder for people with disabilities that rely on keyboard navigation.
The problem is I have yet to find an easy way to differentiate the source of focus events between mouse/keyboard, and clicking as well as keyboard tabbing elements will trigger the focus state.
I know this issue has always been kind of a hot topic, but most sources are several years old, so I will show the main 4 methods I've found on how to achieve keyboard only focus, each with it's own pros and cons:
- The "cheating" method, some websites such as Target.com have the same styles for hovering and focusing, this design choice gave them the option of not needing to remove the focus outline at all.
Pros: Goes around the problem.
Cons: Limited in design options.
button {
width: 180px;
height: 60px;
background: #999;
border: none;
}
button:focus,
button:hover {
outline: 5px solid green;
}
<button>Click or Tab me!</button>
- The JS solution, websites such as Apple.com use ally.js to add
data-focus-method
attributes to their interactable elements. what-input is also quite well known and does a great job, together with various other polyfills.
Pros: Functions properly across all browsers, can be implemented regardless of design.
Cons: Requires extra http requests to fetch .js files, JS is more taxing on performance compared to other solutions.
ally.style.focusSource().current()
button {
width: 180px;
height: 60px;
background: #999;
border: none;
outline: none;
}
html[data-focus-source="key"] button:focus {
outline: 5px solid green;
}
<button>Click or Tab me!</button>
<script src="https://cdn.jsdelivr.net/ally.js/1.4.1/ally.min.js"></script>
:focus-within
the holy grail of elegant solutions.:focus-visible
is basically the keyboard-only version of:focus
and has slowly been made available on most modern browsers (previously:focus-ring
).
Pros: CSS solution, easy on performance, easy to implement.
Cons: Browser support is better but still not ideal with Safari not yet on board.
EDIT: :focus-visible
HAS BEEN ENABLED BY DEFAULT BY SAFARI AS OF MARCH 14 2022
button {
width: 180px;
height: 60px;
background: #999;
border: none;
outline: none;
}
button:focus-visible {
outline: 5px solid green;
}
<button>Click or Tab me!</button>
- The Roman Komarov
tabindex="-1"
hack shows that we can differentiate focus styles by adding aspan
inside ourbutton
elements (technically valid HTML) then giving that spantabindex="-1"
and different focus styling.
Pros: No JS involved, seems to work well across all browsers.
Cons: Requires spamming spans inside all buttons and links on the website, the need to move padding from buttons/links to the inner span, requires adjusting various tracking tags such as gtm.
button {
width: 180px;
height: 60px;
background: #999;
border: none;
outline: none;
}
button > span {
display: flex;
justify-content: center;
height: 100%;
align-items: center;
}
button:focus {
outline: 0;
}
button:focus > span {
outline: 5px solid green;
}
[tabindex="-1"]:focus {
outline: none !important;
}
<button>
<span tabindex="-1">Click or Tab me!</span>
</button>
Now the question would be, is there a better or easier way to achieve this functionality? Is there an industry standard for this that I'm missing?