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

glmvc has just created a new issue for https://github.com/w3c/csswg-drafts:

== [selectors] :focus-visible matching heuristic unclear for non-interactive elements with tabindex="-1" ==
## 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](https://codepen.io/glmvc/full/VYYmgdG).

## Relevant Specification Reference

For user-agent-defined heuristics to [indicate focus](https://drafts.csswg.org/selectors/#indicate-focus), the [Selectors Level 4 specification](https://drafts.csswg.org/selectors/#the-focus-visible-pseudo) 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`](https://drafts.csswg.org/selectors/#focus-pseudo)).
> - 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

- [WebKit Bugzilla 225148: [selectors] :focus-visible and keyboard events](https://bugs.webkit.org/show_bug.cgi?id=225148) — related discussion about `:focus-visible` behavior and keyboard events
- [Mozilla Bugzilla 1688539: New wpt failures in /css/selectors/focus-visible-007.html](https://bugzilla.mozilla.org/show_bug.cgi?id=1688539) — related discussion on WPT failures for `:focus-visible` and keyboard interaction

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12127 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Saturday, 26 April 2025 19:37:25 UTC