[iOS][Fabric] Modal content vanishes from the accessibility tree on repeated present/dismiss cycles (renders on screen, invisible to XCUITest/VoiceOver; dev builds)
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- C++
- Star
- 127k
- Fork
- 25.3k
- Merge trung bình
- 1 ngày 23 giờ
- Pull request đã merge (30 ngày)
- 4
Mô tả
Description
On the new architecture (Fabric), after one successful present/dismiss cycle of a <Modal transparent animationType="slide">, the next presentation renders on screen but registers nothing in the accessibility tree — the AX tree contains only status-bar/system elements. Touch still works: tapping the modal's close button by coordinates dismisses it and fully restores the accessibility tree; presenting again after that heal is blind again.
Reproduced in a bare community-CLI template (no Expo, no navigation library, one Modal), on both 0.86.2 and 0.87.0, using an XCUITest-based driver (Maestro) as the accessibility ground truth:
- Fresh launch → presentation 1 fully accessible → presentation 2 blind. Deterministic across app relaunches (Debug builds).
maestro hierarchyin the blind state shows only the status bar (time, battery, cellular) and the app root — no modal subtree, no base-screen elements.- Dismissing by coordinate tap restores everything; the next presentation is blind again.
- Build-mode asymmetry: Debug reproduces on the 2nd presentation every time; Release stayed clean for 40 cycles on the same app/simulator.
The failure state lives in the app process, not the test driver:
- Each open/close cycle above runs in its own
maestro testinvocation (fresh driver process each time) and the poison persists across them. - In the production app where we first hit this (larger surface, several modal hosts), a fresh XCUITest runner still sees the blind tree, and killing/respawning
testmanagerdmid-blindness changes nothing. There the trigger index varied slightly (2nd–3rd presentation, including cross-modal-host sequences: sheet ✓ → menu ✗, and menu ✓ menu ✓ → sheet ✗), so the exact index looks timing-sensitive; the minimal app settles on "2nd presentation". - The unified log shows correctly paired
kAXUserTestingNotificationpayloads (RCTFabricModalHostViewControllerViewDidAppear ↔ ViewDidDisappear) for every presentation including the blind ones — the modal VC lifecycle looks healthy while AX registration is missing. - Waiting 45s in the blind state does not self-heal; only dismissal heals.
AccessibilityInfo.setAccessibilityFocuson the modal content inonShowdoes not prevent it.
Impact: for assistive-technology users on dev builds, a blind modal's close control is not an accessibility element, so there is no announced way out (we could not script VoiceOver on the simulator to confirm end-to-end; the tree evidence above is from XCUITest snapshots, which share the AX infrastructure). For teams running XCUITest/Maestro/Appium suites against dev builds, any test past the second modal presentation fails on missing elements. Since Release builds did not reproduce in our runs, production users are plausibly unaffected — but we have not found what dev-mode dependency gates the bug, so we can't say that with confidence.
Possibly related, but describing different symptoms: #50152 (invisible undismissed modal layer), #48611, #49717.
Steps to reproduce
- Clone https://github.com/hubyrod/rn-modal-a11y-repro (bare
cli inittemplate + oneApp.tsx: a button presenting a transparent slide-animation Modal with a close button; Maestro flows included). npm install && cd ios && pod install && cd ..npx react-native run-ios --mode Debug(iOS simulator).maestro test cycle.yaml— passes (open → close via accessibility taps).maestro test cycle.yamlagain — fails: the modal is visibly presented but "CLOSE" is not in the accessibility tree.maestro hierarchyshows only status-bar elements.maestro test close-by-point.yaml(coordinate tap) — the modal dismisses and the tree is restored.maestro test open-only.yaml— blind again.
React Native Version
0.86.2 (also reproduced identically on a fresh 0.87.0 template)
Affected Platforms
Runtime - iOS, Build - MacOS
Output of npx react-native info
System:
OS: macOS 26.6.2
CPU: (14) arm64 Apple M4 Pro
Memory: 3.53 GB / 48.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.14.0
path: /Users/hugo/.nvm/versions/node/v22.14.0/bin/node
Yarn:
version: 1.22.22
path: /opt/homebrew/bin/yarn
npm:
version: 11.3.0
path: /Users/hugo/.nvm/versions/node/v22.14.0/bin/npm
Watchman: Not Found
Managers:
CocoaPods:
version: 1.17.0
path: /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 25.5
- iOS 26.5
- macOS 26.5
- tvOS 26.5
- visionOS 26.5
- watchOS 26.5
Android SDK: Not Found
IDEs:
Android Studio: 2025.2 AI-252.28238.7.2523.14688667
Xcode:
version: 26.6/17F113
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.20
path: /usr/bin/javac
Ruby:
version: 4.0.6
path: /opt/homebrew/bin/ruby
npmPackages:
"@react-native-community/cli":
installed: 20.1.0
wanted: 20.1.0
react:
installed: 19.2.3
wanted: 19.2.3
react-native:
installed: 0.86.2
wanted: 0.86.2
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: true
newArchEnabled: true
Simulator: iPhone 17, iOS 26.2 runtime. AX probe: Maestro 2.4.0 (XCUITest driver).
Stacktrace or Logs
No crash. Accessibility hierarchy in the blind state (modal visibly presented,
screenshot available) reduces to status-bar/system elements only:
"accessibilityText" : "02:13" — status bar time
"accessibilityText" : "100 % battery power" — status bar
"accessibilityText" : "Cellular" — status bar
"accessibilityText" : "ModalA11yRepro" — app root node (no children with content)
After a coordinate-tap dismissal, the same probe returns the full tree
(OPEN button, counter text). Unified log shows paired
kAXUserTestingNotification payloads from RCTFabricModalHostViewController
(ViewDidAppear/ViewDidDisappear) for blind presentations as well.
Reproducer
https://github.com/hubyrod/rn-modal-a11y-repro
Screenshots and Videos
Screenshot of the blind state (modal fully rendered, presentations: 2) alongside the status-bar-only hierarchy dump is available on request — the reproducer regenerates both in under a minute.
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.
Hướng nghiên cứu
Bắt đầu với rn-modal-a11y-repro được liên kết: chạy cycle.yaml trong một bản build Debug iOS và so sánh lần hiển thị thứ nhất với lần hiển thị thứ hai bằng Maestro hierarchy. Sau đó, theo dõi vòng đời modal của Fabric xung quanh RCTFabricModalHostViewController và các thông báo accessibility đã được báo cáo; được xem là hoàn thành khi các chu kỳ present/dismiss lặp lại vẫn giữ modal và các phần tử của màn hình nền trong accessibility tree, bao gồm cả flow hiện có được điều khiển bởi accessibility.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- react-native
- Lĩnh vực
- accessibility, mobile
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 52/100