Skip to content

@headlessui/vue Dialog gets stuck unscrollable on iOS after touching outside it (same bug as #3234, fixed for React in #3801 but not ported) #3888

Description

@MILLERMARRU

What package within Headless UI are you using?

@headlessui/vue

What version of that package are you using?

1.7.23 (latest)

What browser are you using?

Safari on iOS

Describe your issue

This looks like the same bug reported for @headlessui/react in #3234 and fixed there in #3801, but the fix never made it into @headlessui/vue.

In packages/@headlessui-vue/src/hooks/document-overflow/handle-ios-locking.ts the touchstart handler on doc decides on every touch whether it started inside an allowed (Dialog) container or not:

d.addEventListener(doc, 'touchstart', (e) => {
  if (e.target instanceof HTMLElement) {
    if (inAllowedContainer(e.target as HTMLElement)) {
      let rootContainer = e.target
      while (rootContainer.parentElement && inAllowedContainer(rootContainer.parentElement)) {
        rootContainer = rootContainer.parentElement!
      }
      d.style(rootContainer, 'overscrollBehavior', 'contain')
    } else {
      d.style(e.target, 'touchAction', 'none')
    }
  }
})

Once you touch outside the Dialog, touch-action: none gets applied to whatever element was touched and never resets. Because the listener stays on document, later touchstart events keep firing, but nothing clears the previously applied touchAction/overscrollBehavior styles before evaluating the new touch, so scrolling and pinch-zooming inside the Dialog stop working entirely until it's closed and reopened.

I compared this against the current @headlessui/react source for the same file and the fix in #3801 wraps the style side effects in a disposable group and disposes the previous group at the start of every touchstart, so the state gets reset and recomputed each time instead of accumulating. The Vue file I read still has the pre-fix version verbatim, no dispose/reset logic anywhere in that handler.

Reproduction

  1. Open a Dialog on iOS Safari.
  2. Scroll and pinch-zoom inside the Dialog, works fine.
  3. Tap or scroll once on the page outside the Dialog.
  4. Try to scroll or pinch-zoom inside the Dialog again, it no longer responds until the Dialog is closed and reopened.

Same repro as #3234, just against @headlessui/vue instead of @headlessui/react.

Expected behavior

Interacting with the Dialog should keep working normally on iOS even after a touch outside it happens, matching the fix already shipped for @headlessui/react in #3801.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions