isFocusVisible is true when clicking label elements (e.g for Checkbox) inside elements with tabIndex
See original GitHub issue🐛 Bug Report
Both useFocusRing and useFocusVisible report isFocusVisible as true when using a mouse to click a checkbox label element if the label has ancestor element with a tabIndex.
🤔 Expected Behavior
isFocusVisible should always be false when clicking with a mouse regardless of whether ancestors have a tabIndex.
😯 Current Behavior
If you click a label around a checkbox or radio input then isFocusVisible returns true. If you click the input element itself, the value is correctly false. If the label doesn’t have an ancestor with a tabIndex the value is correctly false.
This behaviour exists in Chrome and FIrefox but not Safari.
💁 Possible Solution
Sorry, I’m not sure why this happens, other than finding the cause is the tabIndex (even if it’s -1)
🔦 Context
The design of our app has a scrolling aside panel that can appear on the right of the screen. To scroll the main content of the page, instead of making the body scroll, I had to add scrolling to the main content element. To be able to scroll using the keyboard the main content needs to be focused (or contain a focusable element which is focused). If you click the main content and try to scroll using the keyboard, e.g. command + downarrow nothing happens. If I add a tabIndex={-1} to the main content then you can click and start scrolling. I also take advantage of main being focusable because I shift focus to main when you navigate in the SPA. Even without this, there are various other elements that use tabIndex to allow programmatic focus such as popovers. All radio and checkbox inside those elements are drawing keyboard focus when clicked with a mouse. We would prefer to not draw the focus unnecessarily.
💻 Code Sample
See Codesandbox reproduction using a Checkbox component:
https://codesandbox.io/s/serene-bird-51qhbv?file=/src/App.js:648-693
(Sorry, I didn’t use the template, I created the sandbox before I raised this issue)
🌍 Your Environment
| Software | Version(s) |
|---|---|
| react-aria | 3.20.0 |
| Browser | Chrome 106.0.5249.119, FIrefox 106.0 |
| Operating System | MacOS 12.6 |
🧢 Your Company/Team
🕷 Tracking Issue (optional)
Issue Analytics
- State:
- Created a year ago
- Reactions:13
- Comments:8 (3 by maintainers)

Top Related StackOverflow Question
We’re encountering the same focus ring issue with radio buttons and checkboxes. In our case it’s happening when these components are placed inside a dialog. After finding this open issue I see that the useDialog hook is adding
tabindex="-1"to the dialog container, which explains why this bug is being triggered for me.Our implementation of radios/checkboxes/dialogs is pretty much straight from the React Aria docs, so I would expect this bug to be pretty common when placing these components inside a dialog. Just wanted to mention in case that helps with prioritization.
I’ve researched the reason why isFocusVisible becomes true, as @snowystinger said, problem is in handleFocusEvent method. When we have any container with tabIndex=‘-1’, focus event starts firing on this element, and hasEventBeforeFocus sets to false. After that focus event fires on checkbox/input/etc and we get into this if https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/interactions/src/useFocusVisible.ts#L97, which sets modality to ‘virtual’
Maybe we should ignore tabIndex=‘-1’ elements here https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/interactions/src/useFocusVisible.ts#L91?
Possible solution: