adobe / adobe/react-spectrum

[react-aria] FocusScope with contain prevents programmatic scrolling when `prefers-reduced-motion: reduced` and `scroll-behaviour: auto` is given

Open
#6,967 5 comments 0 reactions 0 assignees View on GitHub
accessibility bug
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 (
<>


This won't be scrolled to


Lorem ipsum


Scroll back to top




This will be scrolled to


Lorem ipsum


Scroll back to top



);
}
```

### 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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.