Skip to content

fix(useScrollLock): keep the page where it is while an overlay is open - #8098

Merged
cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/c10-scroll-lock-v2
Nov 3, 2026
Merged

cixzhang merged 1 commit into
meta:mainfrom
vjeux:uikit/c10-scroll-lock-v2

Conversation

@vjeux

@vjeux vjeux commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Behavior-module PR: scroll lock, now MobileNav's too.

Why

Opening a Dialog, BottomSheet, Drawer or Lightbox on a scrolled page moved the page: the lock pinned <body>, so scrollY read 0, the page jumped and Safari's toolbar came back.

What led to it

The pin exists because iOS Safari scrolls an overflow: hidden page under a finger. spec:AST-094 FR19 lets a modal lock cancel a lone finger's page pan.

Options and tradeoffs

  • Restore the offset on close: the jump and the toolbar still happen while the overlay is open.
  • Hold overflow: hidden and cancel the one-finger pans that would reach the page: keeps the offset and toolbar; those pans wait on script.

Why this choice

The second keeps the page still, and the overlay's scrollers, sliders and pinch still get their pans. AST-017 FR3 covers function and semantics, not the pin. AST-094 FR19 gains one sentence: a modal overlay holding the page scroll lock also stops pull-to-refresh and the overscroll bounce. No API change. MobileNav takes the same lock, so its drawer's strip holds too.

How to check it

On a phone, scroll and open a Dialog: the page and Safari's toolbar stay put, a scrim drag holds the page, the dialog's text still scrolls.

Evidence

iOS 26.2 simulator, main and this PR: main this PR

  • 29 unit tests fail with main's lock; 12 of 12 scrim drags in a scrolling container are cancelled (0 before).
  • Size (gzip JavaScript, per the size report): 16 overlay imports +1.6–2.2 KB, once per page (AppShell, SideNav and TopNav through MobileNav's lock); 13 others within 0.06 KB; CSS unchanged.
  • Real-device evidence (AST-094 Platform support): owner hand pass on a real iPhone, 2026-11-03, at c1d9903: Dialog keeps the page and Safari's toolbar put; BottomSheet backdrop drag holds the page; Drawer top pull doesn't bounce; MobileNav strip drag holds the page.
  • Android evidence: Code Quality's emulator recording (Chrome + Gboard; default, Remove animations, slow profile) on Android 16 at c1d9903, the same product code: no pull-to-refresh and no page move on this branch or on main.
  • Range-diff vs c1d9903 (owner-checked): same product code; two tests follow main's AlertDialog change; the docs drop the Android pull-to-refresh claim; the bundle list is deduplicated.
  • Local gates at f03f66e: UI 769 files / 19,477 tests, node 867 files, build, strict lint 0 errors, a11y contracts and this spec in Chromium 81/81, WebKit CI lane ×10 50 passed / 10 skipped, 2-thread pin (one physical core) 4 workers ×3 18/18.

Writing rubric results

  • R1: the record edits use the present tense.
  • R3: the added verification cells name test files.
  • R2, R4, R5, R6: none.

@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 3, 2026 4:14pm UTC

Request Review

@github-actions github-actions Bot added the needs:design-review Affects visuals — Design should review label Oct 24, 2026
@astracat-bot

astracat-bot Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 3, 2026, 4:34 PM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Oct 24, 2026

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new lock keeps the page at its offset under a Dialog on an ordinary page. However, it now shares the root's overflow with MobileNav and changes how pages that style <body> scroll, so this needs changes before it can land. Please fix the following, each with a regression test:

  • MobileNav still writes document.documentElement.style.overflow directly, so a BottomSheet opened from an open MobileNav and closed together with it leaves the root at overflow: clip and the page unscrollable, and closing or unmounting MobileNav drops another overlay's lock; moving MobileNav onto claimRootScrollStyle would fix both.
  • On a page styled html, body { height: 100% } and body { overflow-x: hidden }, opening a Dialog moves the page to the top and it stays there after close, because the root's overflow: hidden stops <body>'s overflow from propagating to the viewport.
  • The touch guard starts its walk from event.target, which a document listener sees as the shadow host, so pans inside a scroller in a shadow root are cancelled; walking event.composedPath() fixes it.

[Automated review]

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · f03f66e

Storybook · Sandbox · built from this PR's head

Current run: The current CI run concluded failure. Current analysis is unavailable.


Generated by PR Enrichment workflow | View current CI run

@cixzhang

Copy link
Copy Markdown
Contributor

Posted by Cindy's agent on her behalf.

Parts of this PR re-implement behavior that already exists, or is being specified, as a shared part of core. Converging keeps one implementation per behavior:

This is about where the behavior lives, not about the PR's intent.

@vjeux

vjeux commented Oct 24, 2026

Copy link
Copy Markdown
Contributor Author

Agreed: one implementation.

  • Writing direction: the guard uses the existing isRtlElement in place of its inline check.
  • Raw touch listeners: the touch guard moves onto the gesture host through a page-scroll hold on the host (under review with the host's owners), so the host stays the single document-level touch owner.
  • Scroll lock: what is left of the guard folds into useScrollLock, which stays the only page scroll lock.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new lock keeps the page at its offset on ordinary pages and on pages whose <body> sets its own overflow, and the shadow-root and stuck-clip cases are fixed. One regression remains: MobileNav still writes document.documentElement.style.overflow itself, so when it closes or unmounts while another overlay is open, for example on a click that closes it and opens a Dialog rendered before it, it clears the lock and lets wheel and keyboard scroll the page behind that overlay, which main prevents today. Please move MobileNav onto claimRootScrollStyle in this PR, or land that part of #8119 with it, with tests for a close and an unmount while another lock holds.

  • The touch guard fixes a pan's axis from its first touchmove, so a horizontal swipe that starts with a tie or a vertical lean (for example 2 px by 2 px) is cancelled even when the strip under the finger can scroll; deciding after a small slop would avoid that.

[Automated review]

vjeux added a commit to vjeux/astryx that referenced this pull request Oct 25, 2026
…held while a Dialog is open; URL text filled)
@vjeux
vjeux force-pushed the uikit/c10-scroll-lock-v2 branch from 6eee897 to 0455794 Compare October 25, 2026 00:35
@vjeux

vjeux commented Oct 25, 2026

Copy link
Copy Markdown
Contributor Author

Done in 55ad273: the guard's own document listeners are gone. useScrollLock holds the page through the gesture host's holdPageScroll, and scrollLockTouchGuard.ts keeps only the pan decision, now with the shared isRtlElement and the host's parentAcrossShadow. This PR now stacks on #8336, which carries the hold.

@github-actions github-actions Bot removed the needs:design-review Affects visuals — Design should review label Oct 25, 2026
@vjeux

vjeux commented Oct 31, 2026

Copy link
Copy Markdown
Contributor Author

Folded in the four points: MobileNav FR3 has its clip follow the page's scroller (the root, or <body> when <body> sets its own overflow); BottomSheetSwitcher FR5 and Drawer FR5 record that pull-to-refresh and the page's bounce stop behind a modal overlay (spec:AST-094 FR19, as behind a UIKit modal presentation); the description marks this a behavior-module PR (scroll lock, MobileNav root claim); and it says simulator and emulator evidence gates the merge while physical iPhone and Android checks gate the release. #8360 now registers the page hold's blocking touchmove as the hold starts under a coarse pointer.

AlertDialog's layout tests stub the page hold, whose pointer read is the lock's, so their guard still sees only the layout's queries.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval waits on an engineering owner's decision on the three current-record amendments this change makes: MobileNav FR3 would have the drawer clip <body> instead of the root on pages whose <body> sets its own overflow, and BottomSheetSwitcher FR5 and Drawer FR5 would add that pull-to-refresh and the page's bounce stop behind a modal overlay, which current spec does not state. An engineering owner records that decision by commenting /approve-spec 4721503acd64d4a362cfe8c8a027036f5763a499 on this pull request (the author may do so as an engineering owner), or asks for the root clip to stay and pull-to-refresh to be left alone. Whatever is decided, before approval the change still needs:

  • a rebase onto a landed #8360, so the diff carries only the scroll-lock commit;
  • real-device recordings for the iOS Safari and Android Chrome claims (an iPhone below iOS 26.4 dragging the scrim right after an overlay opens, a drag that becomes a pinch, and an Android phone pulling down at the top), which AST-094's platform support asks for.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval waits on an engineering owner's decision on the three current-record amendments this change makes: MobileNav FR3 would have the drawer clip <body> instead of the root on pages whose <body> sets its own overflow, and BottomSheetSwitcher FR5 and Drawer FR5 would add that pull-to-refresh and the page's bounce stop behind a modal overlay, which current spec does not state. An engineering owner records that decision by commenting /approve-spec 423aee10cdc73339f4d081587540632fdc6e1700 on this pull request (the author may do so as an engineering owner), or asks for the root clip to stay and pull-to-refresh to be left alone. Whatever is decided, before approval the change still needs:

  • a rebase onto a landed #8360, so the diff carries only the scroll-lock commit and merges cleanly again (it now conflicts with the base in packages/core/src/gestures/GestureHost.ts);
  • real-device recordings for the iOS Safari and Android Chrome claims (an iPhone below iOS 26.4 dragging the scrim right after an overlay opens, a drag that becomes a pinch, and an Android phone pulling down at the top), which AST-094's platform support asks for.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval waits on an engineering owner's decision on the three current-record amendments this change makes: MobileNav FR3 would have the drawer clip <body> instead of the root on pages whose <body> sets its own overflow, and BottomSheetSwitcher FR5 and Drawer FR5 would add that pull-to-refresh and the page's bounce stop behind a modal overlay, which current spec does not state. An engineering owner records that decision by commenting /approve-spec 3e964de1701e0abd7cdae882ea4688957bc55a08 on this pull request (the author may do so as an engineering owner), or asks for the root clip to stay and pull-to-refresh to be left alone. Whatever is decided, before approval the change still needs:

  • a rebase onto a landed #8360, so the diff carries only the scroll-lock commit;
  • real-device recordings for the iOS Safari and Android Chrome claims (an iPhone below iOS 26.4 dragging the scrim right after an overlay opens, a drag that becomes a pinch, and an Android phone pulling down at the top), which AST-094's platform support asks for.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval waits on an engineering owner's decision on the three current-record amendments this change makes: MobileNav FR3 would have the drawer clip <body> instead of the root on pages whose <body> sets its own overflow, and BottomSheetSwitcher FR5 and Drawer FR5 would add that pull-to-refresh and the page's bounce stop behind a modal overlay, which current spec does not state. An engineering owner records that decision by commenting /approve-spec 9821890c91955bbe26f7ab6642e811ea95903bfd on this pull request (the author may do so as an engineering owner), or asks for the root clip to stay and pull-to-refresh to be left alone. Whatever is decided, before approval the change still needs:

  • a rebase onto a landed #8360, so the diff carries only the scroll-lock commit;
  • real-device recordings for the iOS Safari and Android Chrome claims (an iPhone below iOS 26.4 dragging the scrim right after an overlay opens, a drag that becomes a pinch, and an Android phone pulling down at the top), which AST-094's platform support asks for.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval waits on an engineering owner's decision on five current-record edits at this head: MobileNav FR3 (the drawer clips <body> on pages whose <body> sets its own overflow), BottomSheetSwitcher FR5 and Drawer FR5 (pull-to-refresh and the page's bounce stop behind a modal overlay), and the carried #8360 additions to AST-094 FR11 and FR19 and react-component-runtime INV13; an engineering owner records it by commenting /approve-spec 1e22e47861575b299c184427d5261b5e540d20fc on this pull request, which the author may do as an engineering owner. Whatever is decided, these changes are still needed:

  • Land #8360 and rebase, so this pull request carries only the scroll-lock commit.
  • Move MobileNav onto useScrollLock, or say why it cannot use it: its own copy of the lock gets neither the page hold nor the overscroll stop.
  • Reword the changeset's MobileNav sentence, since on the released version closing MobileNav never released another overlay's lock.
  • Add real iPhone and Android recordings for the iOS Safari and Android Chrome claims, as AST-094 asks.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval waits on an engineering owner's decision on five current-record edits at this head: MobileNav FR3 (the drawer clips <body> on pages whose <body> sets its own overflow), BottomSheetSwitcher FR5 and Drawer FR5 (pull-to-refresh and the page's bounce stop behind a modal overlay), and the carried #8360 additions to AST-094 FR11 and FR19 and react-component-runtime INV13. An engineering owner records it by commenting /approve-spec 5adb879f877d1963740a0e0a313716db3f32ee61 on this pull request, which the author may do as an engineering owner. Whatever is decided, these changes are still needed:

  • Land #8360 and rebase, so this pull request carries only the scroll-lock commit.
  • Move MobileNav onto useScrollLock, or say why it cannot use it: its own copy of the lock gets neither the page hold nor the overscroll stop.
  • Reword the changeset's MobileNav sentence, since on the released version closing MobileNav never released another overlay's lock.
  • Add real iPhone and Android recordings for the iOS Safari and Android Chrome claims, as AST-094 asks.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval of this pull request waits on an engineering owner: it amends current MobileNav FR3 (the drawer clips <body> on pages whose <body> sets its own overflow), BottomSheetSwitcher FR5 and Drawer FR5 (pull-to-refresh and the page's bounce stop behind a modal overlay), and the carried #8360 commits add a MUST to AST-094 FR11, a MAY to FR19 and new INV13 text; an engineering owner records the decision by commenting /approve-spec 4b839fd4a06df9ec991e7ad4099136bd0ae90103 on the pull request, and the author may do so as an engineering owner. Whatever that decision, these still need changes before approval:

  • Land #8360 and rebase, so this pull request carries only its own scroll-lock commit.
  • Have MobileNav call useScrollLock instead of keeping its own gutter hold and root claim (MobileNav.tsx:583-619), or say why the shared lock cannot serve it.
  • Reword the changeset's last sentence: the released MobileNav never released another overlay's lock.
  • Add real-device recordings (an iPhone below iOS 26.4 and an Android phone) for the iOS Safari and Android Chrome claims, as AST-094 Platform support asks.

[Automated review]

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval of this pull request waits on an engineering owner: it amends current MobileNav FR3 so the drawer clips <body> on pages whose <body> sets its own overflow, and it adds a pull-to-refresh and bounce stop to BottomSheetSwitcher FR5 and Drawer FR5 that no current spec states (MobileNav PR1 should follow whatever FR3 says). An engineering owner records that decision by commenting /approve-spec d8a610632ded3d8fe360a3de07315ab2937e7100 on the pull request (the author may do so as an engineering owner), or asks for MobileNav to keep clipping the root and the lock to leave pull-to-refresh alone. Two fixes are needed either way:

  • The changeset's last sentence says MobileNav no longer releases another overlay's lock, but the released lock's <body> pin was never touched by MobileNav, and the part it does release, the lock's scrollbar gutter, it still releases under an open lock, leaving scrollbar-gutter: stable on the root afterwards; reword it or route the gutter through the shared lock.
  • spec:AST-094's platform section asks for physical-device evidence for the iOS Safari and Android Chrome claims, which are recorded only on a simulator and an emulator.

[Automated review]

@cixzhang

cixzhang commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

/approve-spec 22f1909

@vjeux

vjeux commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

Threads at 22f1909:

  • Fixed: the gesture host stays out of the overlays' bundles (the page hold asks for a host only if one exists; useScrollLockImports.test.ts pins it), the guard's header, the touch-support bundle cases, the changeset's MobileNav sentence, and MobileNav's gutter, now a claim on the shared stack.
  • Answered: MobileNav keeps its FR3 clip and shares the lock's claim stack for overflow, the overscroll stop and the scrollbar gutter; the description says why.
  • Owner decision: MobileNav FR3 and PR1 (the page's scroller), and the overscroll stop in BottomSheetSwitcher FR5, Drawer FR5 and MobileNav FR3 (a scroll lock under a modal holds the page, spec:AST-094 FR19), wait for an engineering owner's /approve-spec at this head.
  • Moot: the threads on files that left this pull request when fix(core): one click guard for the menu press model and the gesture host #8360 merged (the gesture host, press feedback, the touch presentation, theme adaptations, the page hold, the motion tables, the build plugin and their changesets).

The fixed, answered and moot threads are resolved; the owner-decision threads stay open.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs an engineering owner's decision and a few changes before it can be approved. The scroll-lock fix itself keeps the page in place as the records ask, and the MobileNav, BottomSheetSwitcher and Drawer record edits are approved at this head.

The open decision: the pull-to-refresh and bounce stop also reaches Dialog, AlertDialog, Lightbox, a standalone modal BottomSheet and anyone calling useScrollLock directly, but none of their records states it. Approval needs an engineering owner to provide or identify the record that covers it (for example one sentence in the touch-presentations spec's FR19, or in each component record, recorded by commenting /approve-spec followed by the full head SHA; the author may do this as an engineering owner), or to say that the approved BottomSheetSwitcher and Drawer wording covers those overlays.

Required either way:

  • MobileNav still builds its own page lock beside useScrollLock, and this change copies the overscroll stop into it without the one-finger hold the other modals get, so please have MobileNav call useScrollLock(isOpen) and remove its own claim and gutter hold (MobileNav.tsx:589-610).
  • The changeset, the hook's JSDoc and its doc say the hold works "on touch screens", but it runs only where the primary pointer is coarse, as the new test pins, so please say "where the primary pointer is coarse (phones and tablets)".
  • The iOS Safari and Android Chrome claims rest on the simulator and the emulator, and the touch-presentations spec asks for real-device evidence for them, so please attach recordings from a physical iPhone and Android phone.
  • The branch conflicts with main in docs/specs/components/Drawer/Drawer.spec.md and scripts/touch-support-bundle.test.mjs, so please rebase (the new head will need a fresh /approve-spec).

[Automated review]

@cixzhang

cixzhang commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

/approve-spec bb54740

@vjeux

vjeux commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

At bb54740:

  • MobileNav calls useScrollLock(isOpen), and its own claim and gutter hold are gone, so the strip beside the open drawer gets the one-finger hold too (new test in MobileNavRootScrollClaim.test.tsx).
  • The changeset, the hook's JSDoc and its doc say "where the primary pointer is coarse (phones and tablets)".
  • Rebased onto main (Drawer.spec.md, touch-support-bundle.test.mjs).
  • Device evidence: a person checks this head's preview on a real phone before it leaves draft (owner's gate); device recordings from that check will be attached if the owner agrees.
  • AST-094 FR19 now states the stop for every modal overlay that takes the lock; an engineering owner records it with /approve-spec at this head.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scroll-lock change is close: MobileNav now uses the shared useScrollLock, the changeset names the coarse pointer, and an engineering owner has approved the record edits at this head. Two things are needed before approval:

  • Rebase onto main: docs/specs/components/Drawer/Drawer.spec.md and docs/specs/components/MobileNav/MobileNav.spec.md conflict with main's new Drawer FR20 and MobileNav FR13 rows (keep them), and the rebased head needs a fresh /approve-spec <full head SHA> from an engineering owner (the author may record it as one).
  • Attach physical-device recordings for the iOS Safari and Android Chrome claims (the toolbar staying put, the scrim drag holding, pull-to-refresh stopping), which spec:AST-094 Platform support requires beyond the simulator and emulator recordings.

[Automated review]

@cixzhang

cixzhang commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

/approve-spec c1d9903

@astracat-bot astracat-bot Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close, but two things need fixing before this can merge. The record edits are approved at this head, and the iOS claims have a real-iPhone pass.

  • The changeset and the hook docs say the lock turns off pull-to-refresh in Chrome on Android, but the only checks are emulators, which also report no pull-to-refresh on main; check on a real Android phone in Chrome on main and on this branch, and drop that sentence if main does not reload either (spec:AST-094 Platform support).
  • The branch now conflicts with main in AlertDialog.test.tsx, and since #8083 AlertDialog reaches the gesture host through its own press tracking, so the AlertDialog case in useScrollLockImports.test.ts fails once rebased; rebase, keep both test setups, and drop AlertDialog from that guard's list.

After the rebase, the new head needs its own /approve-spec from an engineering owner.

[Automated review]

gutterRef.current = null;
}
}, []);
// The same page lock as every other modal. Its effect runs before the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking (comment style): "The same page lock as every other modal." restates the call below. Keeping only the reason reads better: "This Effect runs before the open/close Effect below. So it measures the scrollbar gutter before showModal() can hide the scrollbar."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept: the first sentence says why MobileNav takes the shared lock (it is the one every other modal takes), which the call alone doesn't say; the second gives the ordering it relies on.

/** The root scroll styles a caller can hold. */
export interface RootScrollStyle {
/** `hidden` or `clip` stops wheel, keyboard and scrollbar scrolling of the page. */
overflow?: 'hidden' | 'clip';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: no caller passes 'clip' now that MobileNav takes useScrollLock, so overflow could be 'hidden' only.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kept: the claim stack is per value, and its latest-wins and restore-order tests need two overflow values; with 'hidden' alone that path could only be tested through a cast.

@vjeux

vjeux commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

The real-device evidence is now in the description, under Evidence: the owner's hand pass on a real iPhone at c1d9903 (Dialog keeps the page and Safari's toolbar put, the BottomSheet backdrop drag holds the page, the Drawer's top pull doesn't bounce, the MobileNav strip drag holds the page), which the spec owners accept for AST-094 Platform support. The Android pull-to-refresh line follows with the CI owners' emulator check.

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approval waits on an engineering owner: this head rebases the previously approved head onto main, so the five record edits (spec:AST-094 FR19's overscroll sentence, MobileNav FR3 and PR1, BottomSheetSwitcher FR5 and Drawer FR5) need a fresh decision, recorded by commenting /approve-spec 3bba29a0936342a11b4b5aff443f05b01bc919c4 on this pull request. The author may record it as an engineering owner. One fix is still needed after that:

  • The changeset and the useScrollLock docs say the lock turns off pull-to-refresh in Chrome on Android, but the only checks are emulators, which also report no pull-to-refresh on main; check on a real Android phone in Chrome on main and on this branch, and drop that sentence if main does not reload either (spec:AST-094 Platform support).

The rebase cleared the earlier AlertDialog test conflict and import-guard case.

[Automated review]

Comment thread scripts/touch-support-bundle.test.mjs Resolved
The scroll lock no longer pins <body>, which reset the page's offset to 0
(the page jumped and Safari's toolbar came back). It holds `overflow:
hidden` on the page's scroller and, through the gesture host's page hold,
cancels only a one-finger pan that nothing inside the overlay takes: a
scroller, a range input, a pinch or a zoomed page's pan still goes through.
The root's scroll styles become claim stacks (rootScrollStyles), so a lock
and a pull to refresh compose, and MobileNav's clip moves onto them,
with the same stop of pull-to-refresh and the page's bounce. The
scrollbar gutter is a claim on the same stacks, so closing MobileNav under
an open modal keeps the gutter the modal still needs, and the page's own
gutter comes back after both. The top-layer check asks CSS.supports for
`:modal` and `:popover-open` instead of catching errors from `matches`. The
touch module bundle test names useScrollLock and the imports that reach it
as carrying the page hold.

The records state the clip on the page's scroller (MobileNav FR3 and PR1)
and that pull-to-refresh and the page's bounce stop behind a modal overlay
(BottomSheetSwitcher FR5, Drawer FR5, MobileNav FR3), since a scroll lock
under a modal holds the page.

AlertDialog's layout tests stub the page hold, whose pointer read belongs to
the lock, so their guard still sees only the layout's own media queries.
@cixzhang

cixzhang commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

/approve-spec f03f66e

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recommend approval. The five record edits (spec:AST-094 FR19's overscroll sentence, MobileNav FR3 and PR1, BottomSheetSwitcher FR5 and Drawer FR5) are approved by an engineering owner at this head, and the scroll lock now keeps the page at its offset under every modal overlay, with unit tests, browser checks and a real-iPhone pass behind it.

This head also settles both points from the last review: the changeset and the useScrollLock docs no longer claim an Android pull-to-refresh change, and the touch-support bundle list names each import once.

Optional follow-ups (non-blocking):

  • architecture:react-component-runtime INV16 still lists MobileNav's own page scroll lock as a known gap; this change closes it.
  • The holdScrollbarGutter JSDoc example still writes the root's overflow directly; claimRootScrollStyle({overflow: 'hidden'}) is the supported path now.
  • No record states that a one-finger drag on a pinch-zoomed page goes through while the lock holds; one clause in AST-094 FR19 would keep that behavior from being dropped later.

[Automated review]

* const hold = holdScrollbarGutter(document.body);
* body.style.position = 'fixed';
* const hold = holdScrollbarGutter(document.documentElement);
* document.documentElement.style.overflow = 'hidden';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: this example writes the root's overflow directly, which rootScrollStyles now owns. const release = claimRootScrollStyle({overflow: 'hidden'}); would show the supported path.

This branch was successfully deployed

1 active deployment
Preview — f03f66e4 Deployed Nov 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) CLA Signed This label is managed by the Meta Open Source bot. needs:design-review Affects visuals — Design should review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants