Tabindex, programmatic focus, and outline styles
For accessibility reasons, I sometimes need to programmatically focus on elements that would not normally be focusable. Two noteable examples…
- Skip Nav links
- Shifting focus to the
h1heading in an SPA after a route change
In both examples, you need to add a [tabindex] attribute with a value of -1 to the element in order for it to receive focus.
<a href="#main">Skip to Main Content</a>
<!-- Nav Menu Stuff... -->
<main id="main" tabindex="-1">
<!-- ... -->
</main>By default, when the content in question receives focus, it gets a big blue focus ring around it.
That ring is an important visual indicator about what has focus for people who navigate by keyboard. But it can look “off” for focus who navigate with a mouse.
(This is generally a bigger issue with SPAs and heading focus than a skip nav link, which is typically only used by keyboard users.)
To “fix” this without breaking accessibility for people who navigate with a keyboard, you can hide the outline on :focus, and include it on :focus-visible instead.
/**
* Only style programmatically focused elements if needed
* @link https://code.google.com/p/chromium/issues/detail?id=37721
*/
[tabindex="-1"]:focus {
outline: none;
}
[tabindex="-1"]:focus-visible {
outline: 2px solid var(--color-focus);
}This pseudo-class gets applied to an element when the browser/User Agent determines (based on user heuristics or behavior) that they would benefit from visible focus indicators.
In my experience, if the behavior that triggered the focus event was triggered by a mouse, :focus-visible is not applied. If it was triggered by a keyboard, it is.