Provide a general summary of the issue here
There is an inconsistency in ariaHideOutside.ts between how the data-react-aria-top-layer attribute is checked during the initial DOM scan vs. when handling dynamically inserted nodes via MutationObserver.
Initial scan (line 87) — matches attribute presence (any value):
root.querySelectorAll('[data-live-announcer], [data-react-aria-top-layer]')
This CSS attribute selector matches data-react-aria-top-layer regardless of its value ("", "true", anything).
MutationObserver (lines 178, 221) — requires exact value "true":
node.dataset.reactAriaTopLayer === 'true'
When the attribute is set as an empty string (e.g. data-react-aria-top-layer=""), dataset.reactAriaTopLayer is "", which does not equal 'true'.
Note: data-live-announcer is always set to "true" in LiveAnnouncer.tsx, so the === 'true' check is correct for it. The inconsistency only affects data-react-aria-top-layer.
🤔 Expected Behavior?
Both the initial scan and MutationObserver should use the same logic to detect the data-react-aria-top-layer attribute. Elements with this attribute (regardless of value) should be kept visible and not hidden with aria-hidden="true".
😯 Current Behavior
When a top-layer element (e.g. toast) is dynamically inserted into the DOM while ariaHideOutside is active, and its data-react-aria-top-layer attribute value is not exactly the string "true", the MutationObserver fails to recognize it as a visible node. The element gets incorrectly hidden with aria-hidden="true".
Current internal usage in useToastRegion.ts passes true (boolean) in JSX props ('data-react-aria-top-layer': true), which React renders as the string "true", so it works by coincidence. But the inconsistency is a latent bug for any consumer that sets the attribute differently (e.g. via setAttribute('data-react-aria-top-layer', '')).
💁 Possible Solution
Change the MutationObserver checks for data-react-aria-top-layer to use !== undefined instead of === 'true' to match the CSS attribute presence selector behavior:
node.dataset.reactAriaTopLayer !== undefined
This applies to both MutationObserver callbacks (lines 178 and 221).
Alternatively, the initial scan at line 87 could be changed to [data-react-aria-top-layer="true"] to match the MutationObserver — but using attribute presence (!== undefined) is more robust and forgiving.
🔦 Context
No response
🖥️ Steps to Reproduce
- Call
ariaHideOutside on some target elements
- Dynamically insert a new element with
data-react-aria-top-layer="" (empty string) into the DOM
- Observe that the MutationObserver does NOT add it to
visibleNodes because node.dataset.reactAriaTopLayer === 'true' evaluates to false
- The element gets incorrectly hidden with
aria-hidden="true"
Compare with: if the element existed before ariaHideOutside was called, the initial scan's querySelectorAll('[data-react-aria-top-layer]') would correctly match it regardless of attribute value.
Version
react-aria-components@1.16.0
What browsers are you seeing the problem on?
Chrome
If other, please specify.
No response
What operating system are you using?
macOS
🧢 Your Company/Team
No response
🕷 Tracking Issue
No response
Provide a general summary of the issue here
There is an inconsistency in
ariaHideOutside.tsbetween how thedata-react-aria-top-layerattribute is checked during the initial DOM scan vs. when handling dynamically inserted nodes via MutationObserver.Initial scan (line 87) — matches attribute presence (any value):
This CSS attribute selector matches
data-react-aria-top-layerregardless of its value ("","true", anything).MutationObserver (lines 178, 221) — requires exact value
"true":When the attribute is set as an empty string (e.g.
data-react-aria-top-layer=""),dataset.reactAriaTopLayeris"", which does not equal'true'.Note:
data-live-announceris always set to"true"inLiveAnnouncer.tsx, so the=== 'true'check is correct for it. The inconsistency only affectsdata-react-aria-top-layer.🤔 Expected Behavior?
Both the initial scan and MutationObserver should use the same logic to detect the
data-react-aria-top-layerattribute. Elements with this attribute (regardless of value) should be kept visible and not hidden witharia-hidden="true".😯 Current Behavior
When a top-layer element (e.g. toast) is dynamically inserted into the DOM while
ariaHideOutsideis active, and itsdata-react-aria-top-layerattribute value is not exactly the string"true", the MutationObserver fails to recognize it as a visible node. The element gets incorrectly hidden witharia-hidden="true".Current internal usage in
useToastRegion.tspassestrue(boolean) in JSX props ('data-react-aria-top-layer': true), which React renders as the string"true", so it works by coincidence. But the inconsistency is a latent bug for any consumer that sets the attribute differently (e.g. viasetAttribute('data-react-aria-top-layer', '')).💁 Possible Solution
Change the MutationObserver checks for
data-react-aria-top-layerto use!== undefinedinstead of=== 'true'to match the CSS attribute presence selector behavior:This applies to both MutationObserver callbacks (lines 178 and 221).
Alternatively, the initial scan at line 87 could be changed to
[data-react-aria-top-layer="true"]to match the MutationObserver — but using attribute presence (!== undefined) is more robust and forgiving.🔦 Context
No response
🖥️ Steps to Reproduce
ariaHideOutsideon some target elementsdata-react-aria-top-layer=""(empty string) into the DOMvisibleNodesbecausenode.dataset.reactAriaTopLayer === 'true'evaluates tofalsearia-hidden="true"Compare with: if the element existed before
ariaHideOutsidewas called, the initial scan'squerySelectorAll('[data-react-aria-top-layer]')would correctly match it regardless of attribute value.Version
react-aria-components@1.16.0
What browsers are you seeing the problem on?
Chrome
If other, please specify.
No response
What operating system are you using?
macOS
🧢 Your Company/Team
No response
🕷 Tracking Issue
No response