Skip to content

Fix bottom sheet showing scroll position of triggering page#193

Open
noah44846 wants to merge 1 commit intohotwired:mainfrom
noah44846:fix/bottom-sheet-scroll-position
Open

Fix bottom sheet showing scroll position of triggering page#193
noah44846 wants to merge 1 commit intohotwired:mainfrom
noah44846:fix/bottom-sheet-scroll-position

Conversation

@noah44846
Copy link
Copy Markdown

Problem

When opening a bottom sheet from a scrolled page, the bottom sheet inherits the scroll position of the page that triggered it. The content appears offset — scrolled down by however far the user had scrolled on the underlying page.

Screen_recording_20260312_172301.webm

The screen recording was made on v1.2.6 and the Demo app was using a version of hotwire-native-demo with the path config modified so that the modal pages are shown in a bottom sheet (uri: "hotwire://fragment/web/modal/sheet")

I think this happens because the WebView carries its native scrollY when it moves between fragment containers. For normal page navigation this is not an issue: Turbo's JS performScroll() calls window.scrollTo(0, 0) which successfully resets the scroll. But in a bottom sheet, the WebView is sized with WRAP_CONTENT inside a NestedScrollView — in this layout the WebView is not internally scrollable from the JS perspective, so window.scrollTo() has no effect. The native scrollY persists and gets rendered as an offset inside the NestedScrollView.

Proposed fix

Reset the WebView's native scroll in visitRendered(), guarded by a check that the WebView's parent is a ScrollingView. At this point in the lifecycle, Turbo has already called changeHistory() which updates history.restorationIdentifier to the new page's own ID — so the scroll event that fires from scrollTo(0, 0) updates the correct page's restoration data and does not corrupt the underlying page's saved scroll position. The reset also happens before removeTransitionalViews(), so the progress view is still visible and there is no visible flash.

Screen recording with fix:

Screen_recording_20260312_173620.webm

When the shared WebView moves into a ScrollingView container (bottom
sheet), its native scrollY carries over from the previous page. Turbo's
JS performScroll() cannot reset it because a WRAP_CONTENT WebView inside
a NestedScrollView is not scrollable from JS's perspective. Reset the
native scroll in visitRendered(), after Turbo has updated
history.restorationIdentifier to the current page's ID, so the resulting
scroll event does not corrupt the previous page's saved scroll position.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant