Skip to content

[selectors] :focus-visible matching heuristic unclear for non-interactive elements with tabindex="-1" #12127

Open
@glmvc

Description

@glmvc

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

  1. Non-interactive elements without tabindex (e.g., <section>)
    • Cannot receive focus.
    • No issues observed, expected behavior.
  2. 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.
  3. 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.
  4. 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.
  5. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions