@@ -53,12 +53,7 @@ export const HModalPanel = component$((props: PropsOf<'dialog'>) => {
5353
5454 if ( ! panelRef . value ) return ;
5555
56- // Initialize the scroll locker once. `@fluejs/noscroll` keeps the panel
57- // itself scrollable/interactive (incl. touch dragging on iOS Safari) while
58- // the page behind it is locked — unlike body-scroll-lock-upgrade, which
59- // blocked movement-based touch inside the modal on iOS (#1113).
6056 if ( ! isInitialized . value ) {
61- markScrollable ( panelRef . value ) ;
6257 isInitialized . value = true ;
6358
6459 const noScroll = createNoScroll ( {
@@ -74,6 +69,20 @@ export const HModalPanel = component$((props: PropsOf<'dialog'>) => {
7469
7570 if ( isOpen ) {
7671 await showModal ( panelRef . value ) ;
72+
73+ // noscroll only lets touch gestures scroll elements it has marked.
74+ // Mark the panel and any inner scroll container (e.g. a scrollable body
75+ // with a sticky header/footer) so they remain touch-scrollable on iOS;
76+ // otherwise the gesture resolves to the non-scrollable dialog and gets
77+ // prevented. Re-scanned on each open to pick up the current content.
78+ markScrollable ( panelRef . value ) ;
79+ panelRef . value . querySelectorAll < HTMLElement > ( '*' ) . forEach ( ( el ) => {
80+ const { overflowX, overflowY } = getComputedStyle ( el ) ;
81+ if ( / ( a u t o | s c r o l l | o v e r l a y ) / . test ( `${ overflowX } ${ overflowY } ` ) ) {
82+ markScrollable ( el ) ;
83+ }
84+ } ) ;
85+
7786 disablePageScroll . value ?.( ) ;
7887 activateFocusTrap ( focusTrap ) ;
7988 } else {
0 commit comments