FocusScope's Tab interception defeats browser-native keyboard-focusable scroll containers, scrollable content in a Modal Dialog cannot be scrolled by keyboard (WCAG 2.1.1)
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 52/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Ít trao đổi
- Công nghệ
- react, typescript
- Lĩnh vực
- accessibility, frontend
Hướng nghiên cứu
Bắt đầu với tabbable-tree walker của FocusScope và chạy bản tái hiện StackBlitz được liên kết trong Chrome hoặc Firefox. So sánh hành vi tab đối với phần thân hộp thoại có thể cuộn khi có và không có tabIndex={0}, sau đó kiểm tra hành vi FocusScope liên quan được ghi nhận trong các issue #6729 và #9570. Được xem là hoàn tất khi người dùng bàn phím có thể tiếp cận và cuộn tự nhiên một container hộp thoại bị tràn mà không cần một tabIndex cố định không cần thiết.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Provide a general summary of the issue here
Since Chrome 127 and Firefox, browsers automatically include scrollable containers without focusable children in the sequential tab order, so keyboard users can focus them and scroll with arrow keys (Chrome feature: keyboard-focusable scrollers). Inside a Dialog, FocusScope intercepts every Tab keypress (preventDefault) and moves focus itself using its own focusable-tree walker. That walker only recognizes conventionally focusable elements, so an implicitly keyboard-focusable scroll container is skipped entirely. As a result, a dialog with a scrollable content region (pinned header/footer with overflow: auto body, a very common dialog layout) is impossible to scroll with the keyboard: focus can never reach the scroller, and there is nothing arrow keys will scroll. This fails WCAG 2.1.1 (Keyboard) and axe's scrollable-region-focusable rule.
🤔 Expected Behavior?
Tab (as managed by FocusScope inside a Dialog) should be able to reach a scrollable container that the browser itself would place in the tab order, matching native behavior outside the focus trap. Once focused, arrow keys / PageUp / PageDown / Home / End scroll it natively.
😯 Current Behavior
Tab cycles only between conventionally focusable elements (buttons, inputs, [tabindex]). The scrollable region is unreachable; keyboard scrolling is impossible.
Reproduced by Claude Fable via iinstrumented reproduction in Chromium 149:
Without tabIndex on the scroller: Tab visits only the dialog's buttons (keydown for Tab arrives with defaultPrevented: true at the document, FocusScope moved focus itself). After ArrowDown ×2 + PageDown, the scroller's scrollTop is still 0.
With an explicit tabIndex={0} on the same element: Tab reaches it and native scrolling works immediately (ArrowDown → 40, PageDown → 550, End → bottom).
The same markup outside a FocusScope is keyboard-scrollable in Chrome 127+/Firefox with no tabindex, because native Tab handling includes the scroller.
💁 Possible Solution
Teach the focusable/tabbable tree walker used by FocusScope to treat an element as tabbable when the browser would: a scroll container (computed overflow scrollable and scrollHeight > clientHeight / scrollWidth > clientWidth) containing no other focusable elements, i.e. mirror Chrome's keyboard-focusable-scrollers heuristic. Alternatively (or until then), document that scrollable regions inside Dialog require an explicit tabIndex={0}.
Workaround we use today: hardcode tabIndex={0} on the dialog body. It works, but it adds a permanent tab stop even when the content doesn't overflow, which browsers' native heuristic avoids.
🔦 Context
We build a design-system Dialog on react-aria-components with a pinned header/footer and a scrolling body
.dialog {
display: flex;
flex-direction: column;
max-height: 80dvh;
overflow: hidden;
}
.dialog__body {
overflow-y: auto;
}
Our Storybook axe checks flag scrollable-region-focusable, and manual testing confirms keyboard users genuinely cannot scroll long dialog content (e.g. terms of service, multi-step forms). Comparable dialog implementations whose focus traps preserve native Tab handling via sentinel/guard elements get this right automatically in Chrome/Firefox — the regression is specific to focus traps that re-implement Tab navigation, which is what react-aria's FocusScope does. Related: #6729 (native video controls skipped by the same walker), #9570 (pointer interaction with scrollable content under contain).
🖥️ Steps to Reproduce
Created with the help of Claude AI.
https://stackblitz.com/edit/rac-dialog-scroll-issue?file=src%2FApp.tsx
- Open the dialog with the keyboard (Enter on the trigger).
- Press Tab repeatedly — focus only ever reaches the Close button; the scrollable is never focused.
- Press ArrowDown / PageDown — nothing scrolls (scrollTop stays 0).
- Control: render the same scrollable outside the Modal in Chrome 127+ — Tab focuses it natively and arrow keys scroll it.
- Control 2: add tabIndex={0} to the inside the Modal — Tab now reaches it and keyboard scrolling works.
Version
react-aria-components: 1.19.0 (react-aria 3.50.0)
What browsers are you seeing the problem on?
Chrome, Firefox
If other, please specify.
Browsers: Chromium 149.0.7827.55 (behavior applies to Chrome 127+ and Firefox, which ship keyboard-focusable scrollers; Safari lacks the native feature so it's equally broken there either way)
What operating system are you using?
Windows 11
🧢 Your Company/Team
No response
🕷 Tracking Issue
No response
- Ngôn ngữ chính
- TypeScript
- Star
- 16k
- Fork
- 1.6k
- Merge trung bình
- 4 ngày 3 giờ
- Pull request đã merge (30 ngày)
- 39
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của adobe/react-spectrum
-
@internationalized/number: NumberParser misparses Arabic-digit numbers that use . , or ، (sd parses ١٬٢٣٤.٥ as 12345)Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
adobe/react-spectrum#10936 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
PreviewTrigger: the preview dialog has no accessible nameCó thể đã có người làm @therealemmanueloppong đã nhận 2 ngày trước. Đang mởaccessibility bug PreviewTrigger
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
adobe/react-spectrum#10917 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
S2 quiet Picker menu is always 192px wide: its width calc() has no unitCó thể đã có người làm @darshpatel1052 đã nhận 5 ngày trước. Đang mởwaiting on author
Độ khó 2/5 Dưới một giờ Mức phù hợp với người mới 88/100
adobe/react-spectrum#10909 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
DatePicker omits timezone for initially empty fields despite zoned placeholderValueCó thể đã có người làm @mrlexcoder đã nhận 5 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
adobe/react-spectrum#10900 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Separator adds a redundant role="separator" to its default <hr>Có thể đã có người làm @Janani-bn đã nhận 5 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
adobe/react-spectrum#10894 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của adobe/react-spectrum
Issue tương tự
-
Add: Shop On TV SDĐang mởcheck:passed streams:add
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 64/100
Maintainer thường phản hồi trong vòng 1 ngày
-
bug implicit-state
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
-
book-layout.spec fails when the similar-books rail holds other projects' booksCó thể đã có người làm @praetorianer777 đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
praetorianer777/GoTome#373 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
Tencent/tdesign-react#4465 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
area:cards enhancement ready-for-agent
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
fil-donadoni/tolaria#8883 ·
Maintainer thường phản hồi trong vòng 1 ngày