fix(fabric): force overlay scrollbar style in ScrollView#2907
Merged
fix(fabric): force overlay scrollbar style in ScrollView#2907
Conversation
|
Collaborator
Author
|
/backport 0.81-stable |
Backport results
|
Force NSScrollerStyleOverlay on Fabric ScrollViews to avoid layout issues with legacy (always-visible) scrollbars. Legacy scrollbars sit inside the NSScrollView frame and reduce the clip view's visible area, which would require compensating padding in the Yoga shadow tree. Overlay scrollers float above content, so no layout compensation is needed. Additional fixes: - Remove autoresizingMask from documentView to prevent AppKit frame corruption during tile/resize - Remove the layoutSubviews workaround (no longer needed without autoresizingMask) - Add [_scrollView tile] after content size updates to re-evaluate scroller visibility - Fix hit testing: check NSScroller before content subviews so scrollbar clicks aren't swallowed by full-width content views Fixes #2857 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3f66ad7 to
ef37f10
Compare
4 tasks
Saadnajmi
commented
Apr 10, 2026
...s/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
Show resolved
Hide resolved
...s/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
Outdated
Show resolved
Hide resolved
...s/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
Outdated
Show resolved
Hide resolved
...s/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
Show resolved
Hide resolved
...s/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm
Outdated
Show resolved
Hide resolved
JunielKatarn
approved these changes
Apr 11, 2026
- Minimize verbose comments throughout - Remove explicit [_scrollView tile] call in updateState: (unnecessary with overlay scrollbar style) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Saadnajmi
added a commit
that referenced
this pull request
Apr 11, 2026
## Summary Backport of #2907 to `0.81-stable`. - Force `NSScrollerStyleOverlay` on Fabric ScrollViews to fix layout overflow on first render - Remove `autoresizingMask` from documentView to prevent AppKit frame corruption - Fix scrollbar click hit testing (check `NSScroller` before content subviews) - Re-force overlay style when the system "Show scroll bars" preference changes at runtime ## Why force overlay instead of supporting legacy scrollbars? 1. **Every other platform uses overlay scrollbars.** iOS, Android, and web all render scrollbar indicators that float above content. Forcing overlay aligns macOS with every other React Native platform. 2. **Supporting legacy scrollbars required invasive changes to ReactCommon.** The alternative required cached atomic values, custom shadow node constructors, and padding adjustments in the Yoga layout pass — a significant cross-platform change for a single-platform edge case. 3. **Apple themselves call non-overlay scrollbars "legacy."** The API is literally `NSScrollerStyleLegacy`. Mac Catalyst doesn't even respect this preference (always uses overlay). ## Test plan - [x] Verified on macOS with "Show scroll bars: Always" — no overflow on first render - [x] Verified switching system preference at runtime doesn't bring back legacy scrollbars - [x] Verified scrollbar clicks work - [x] Verified window resize doesn't cause overflow Fixes #2857 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
NSScrollerStyleOverlayon Fabric ScrollViews to fix layout overflow on first renderautoresizingMaskfrom documentView to prevent AppKit frame corruptionNSScrollerbefore content subviews)Why force overlay instead of supporting legacy scrollbars?
Every other platform uses overlay scrollbars. iOS, Android, and web all render scrollbar indicators that float above content. macOS is the only platform where scrollbars can sit inside the frame and reduce the visible content area. Forcing overlay aligns macOS behavior with every other React Native platform.
Supporting legacy scrollbars required invasive changes to ReactCommon. The alternative approach required adding cached atomic values, custom shadow node constructors, and padding adjustments in the Yoga layout pass at the
ScrollViewShadowNodeC++ layer — a significant cross-platform change to support a single-platform edge case.Apple themselves call non-overlay scrollbars "legacy." The API is literally
NSScrollerStyleLegacy. Mac Catalyst doesn't even respect this system preference (it always uses overlay). SwiftUI does respect it, but SwiftUI also has the advantage of proposing clip view size to children — something React Native's layout system doesn't do.Test plan
Fixes #2857
🤖 Generated with Claude Code