[react-aria] FocusScope with contain prevents programmatic scrolling when `prefers-reduced-motion: reduced` and `scroll-behaviour: auto` is given
- Dominant language
- TypeScript
- Stars
- 15.9k
- Forks
- 1.6k
- Avg merge
- 3d 9m
- Merged PRs (30d)
- 59
Description
### Provide a general summary of the issue here
Consider the following code example
```tsx
Hello, world!
{/** Lots of text */}
Scroll back to top
```
The issue seems to only occur when `contain` is set to `true` on the `FocusScope`
### 🤔 Expected Behavior?
I'd expect that when I click on a hash anchor link, it should be consistent with the default browser behaviour, scrolling to the targeted element.
### 😯 Current Behavior
If I have my acessibility settings to have `prefers-reduced-motion` set to `reduce`, with the scroll behaviour of the given container being `auto` then clicking on the "Scroll back to top" link does nothing.
### 💁 Possible Solution
~~Adding an onclick event for the link similar to the following can resolve the issue.~~ This won't work in Safari, the FocusScope will take control back to the `link` and prevent scroll.
```ts
(event) => {
const href = e.currentTarget.href;
if (!href.startsWith('#')) return;
document.querySelector(href)?.scrollIntoView();
}
```
Though this isn't a reasonably scaleable solution.
I've tried using `focus()` to force scroll within the FocusScope but I don't like to think what the a11y implications will be.
```ts
const scrollToHashHref = (event: React.MouseEvent) => {
const href = event.currentTarget.getAttribute('href')
if (!href || !href.startsWith('#')) return
// Href targets an id, so let's try scroll to that element
const ref = document.querySelector(href)
if (!(ref instanceof HTMLElement)) return
// We need to ensure the element is focusable, focus on it, and then restore it's original focusability
// - FocusScope will refocus on the link, preventing scroll
// - FocusScope will prevent using `focus()` on non-focusable elements in Safari
const initialTabIndex = ref.getAttribute('tabindex')
ref.setAttribute('tabindex', '-1')
ref.focus()
if (initialTabIndex == null) {
ref.removeAttribute('tabindex')
} else {
ref.setAttribute('tabIndex', initialTabIndex)
}
}
```
### 🔦 Context
_No response_
### 🖥️ Steps to Reproduce
Using react-aria
```tsx
export default function App() {
return (
<>
);
}
```
### Version
3.34.1
### 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_
Contributor guide
Research direction
Start with the FocusScope component and reproduce the difference between contain and contain={false} using the provided scrollable-container example, reduced-motion preference, and auto scroll behavior. Trace how FocusScope handles focus after the hash link is clicked, then verify that the targeted element scrolls consistently without breaking focus behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- accessibility, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100