Introduction
While testing across major browsers, I observed that, in specific cases, the :focus-visible pseudo-class matches non-interactive elements with a tabindex="-1" attribute after pointer interaction (e.g., mouse click) followed by non-pointer interaction (e.g., keystroke).
This behavior varies between browsers, and it is unclear whether the current specification intends for :focus-visible to match on such elements under these circumstances.
I've created a demo on CodePen to illustrate the different scenarios and behaviors.
Relevant Specification Reference
For user-agent-defined heuristics to indicate focus, the Selectors Level 4 specification provides non-normative suggestions such as:
- If the user interacts with the page via keyboard or some other non-pointing device, indicate focus. (This means keyboard usage may change whether this pseudo-class matches even if it doesn’t affect
:focus).
- If the user interacts with the page via a pointing device (mouse, touchscreen, etc.) and the focused element does not support keyboard input, don’t indicate focus.
- If the previously-focused element indicated focus, and a script causes focus to move elsewhere, indicate focus on the newly focused element.
However, the behavior for non-interactive elements defined with tabindex="-1", which can be focused programmatically or through pointer interaction but are not accessible by keyboard navigation, remains unclear, as reflected by the different browser implementations.
Observed Behaviors and Differences
- Non-interactive elements without
tabindex (e.g., <section>)
- Cannot receive focus.
- No issues observed, expected behavior.
- Non-interactive elements with
tabindex="0" (e.g., <section tabindex="0">)
- Focusable by pointer interaction (e.g., mouse click) and non-pointer interaction (e.g., keyboard tabbing).
- After pointer interaction (
:focus via mouse click), a specific non-pointer interaction (e.g., keystroke) triggers :focus-visible in Chrome and Safari, but not in Firefox.
- Non-interactive elements with
tabindex="-1" (e.g., <section tabindex="-1">)
- Focusable via script or pointer interaction, but not via keyboard navigation.
- The same behavior occurs as with
tabindex="0".
- It remains unclear whether this behavior is intended, since
tabindex="-1" elements are not keyboard-focusable.
- Interactive and focusable child elements within
tabindex="-1" elements (e.g., <section tabindex="-1"> containing a <button>)
- Interaction with a focusable child element (e.g.,
<a>, <button>, or <input type="checkbox">) behaves differently across browsers:
- Firefox: The child element receives
:focus on pointer interaction (click), but does not change to :focus-visible on non-pointer interaction (key activation).
- Chrome: Non-pointer interaction (key activation) after pointer interaction (click) triggers
:focus-visible on the child element.
- Safari (platform-dependent): The parent with
tabindex="-1" receives :focus instead of the child element on pointer interaction (click).
- Interaction with a text input element (e.g.,
<input type="text">)
- Consistent and expected behavior across Chrome, Safari, and Firefox:
:focus-visible appears after both pointer and non-pointer interaction because the element awaits keyboard input.
- Programmatic focus on the parent element (
<section tabindex="-1">) via button (<button type="button">) activation:
- Consistent and expected behavior across Chrome, Safari, and Firefox (in line with spec suggestions): On pointer interaction (click), the parent element receives
:focus; on non-pointer activation (keyboard), it receives :focus-visible.
- Keystroke exceptions
- Modifier keys (e.g., fn, Control, Option/Alt, Command) and function keys (e.g., F1–F12) generally do not trigger
:focus-visible when an element is already focused (:focus).
- Safari differs slightly: Pressing the Shift key does not trigger
:focus-visible, while in Chrome it does.
I am not claiming that any particular behavior is incorrect. I am seeking clarification to ensure consistent behavior across user agents.
Further Resources
Introduction
While testing across major browsers, I observed that, in specific cases, the
:focus-visiblepseudo-class matches non-interactive elements with atabindex="-1"attribute after pointer interaction (e.g., mouse click) followed by non-pointer interaction (e.g., keystroke).This behavior varies between browsers, and it is unclear whether the current specification intends for
:focus-visibleto match on such elements under these circumstances.I've created a demo on CodePen to illustrate the different scenarios and behaviors.
Relevant Specification Reference
For user-agent-defined heuristics to indicate focus, the Selectors Level 4 specification provides non-normative suggestions such as:
However, the behavior for non-interactive elements defined with
tabindex="-1", which can be focused programmatically or through pointer interaction but are not accessible by keyboard navigation, remains unclear, as reflected by the different browser implementations.Observed Behaviors and Differences
tabindex(e.g.,<section>)tabindex="0"(e.g.,<section tabindex="0">):focusvia mouse click), a specific non-pointer interaction (e.g., keystroke) triggers:focus-visiblein Chrome and Safari, but not in Firefox.tabindex="-1"(e.g.,<section tabindex="-1">)tabindex="0".tabindex="-1"elements are not keyboard-focusable.tabindex="-1"elements (e.g.,<section tabindex="-1">containing a<button>)<a>,<button>, or<input type="checkbox">) behaves differently across browsers::focuson pointer interaction (click), but does not change to:focus-visibleon non-pointer interaction (key activation).:focus-visibleon the child element.tabindex="-1"receives:focusinstead of the child element on pointer interaction (click).<input type="text">):focus-visibleappears after both pointer and non-pointer interaction because the element awaits keyboard input.<section tabindex="-1">) via button (<button type="button">) activation::focus; on non-pointer activation (keyboard), it receives:focus-visible.:focus-visiblewhen an element is already focused (:focus).:focus-visible, while in Chrome it does.Further Resources
:focus-visiblebehavior and keyboard events:focus-visibleand keyboard interaction