Skip to content

Conversation

RobinMalfait
Copy link
Member

@RobinMalfait RobinMalfait commented Sep 19, 2025

This PR fixes an issue where if you have an open Dialog on iOS that once you interacted with an area otuside of the dialog, you could no longer scroll or zoom the dialog itself anymore until it closed and reopened.

The reason this was happening is that on touchstart we checked whether we are in an allowed area or not.

  • If we are in an allowed area, add overscroll-behavior: contain to the scrollable area to prevent scrolling the body behind it.
  • If we are not in an allowed area, add touch-action: none to the element you were touching to prevent any other touch events from being fired.

The problem with this is that we never reset the state until the dialog is closed (and eventually unmounted).

So to solve the problem, every time we get a touchstart event, we reset those CSS properties to their previous values, and then check again whether we are in an allowed area or not.

Note: the touchstart event listener is set on the document itself, so we still get the event even if touch-action: none was set on the target element.

Test plan

Made 2 videos with a before / after comparison. The reproduction used is from #3234. The steps I'm applying here are:

  1. Open the dialog by tapping on the toggle button
  2. Scroll inside the dialog
  3. Pinch to zoom inside the dialog
  4. Tap outside / scroll outside the dialog to show that you can't with the outside
  5. Scroll inside the dialog again
  6. Pinch to zoom inside the dialog again

Before:

  1. ✅ Open the dialog by tapping on the toggle button
  2. ✅ Scroll inside the dialog
  3. ✅ Pinch to zoom inside the dialog
  4. ✅ Tap outside / scroll outside the dialog to show that you can't with the outside
  5. ❌ Scroll inside the dialog again
  6. ❌ Pinch to zoom inside the dialog again
bad.mp4

After:

  1. ✅ Open the dialog by tapping on the toggle button
  2. ✅ Scroll inside the dialog
  3. ✅ Pinch to zoom inside the dialog
  4. ✅ Tap outside / scroll outside the dialog to show that you can't with the outside
  5. ✅ Scroll inside the dialog again
  6. ✅ Pinch to zoom inside the dialog again
good.mp4

Fixes: #3234
Closes: #3602

Before, when you were scrolling or interacting with an open dialog on
iOS, then the moment you scroll we check whether you're allowed to or
not. Scrolling inside the Dialog is fine, outside is not.

One thing we did is if you are outside, we use `touch-action: 'none'`.
The problem is that we never reset this value while the dialog is open.
This results in an issue where you cannot interacting with the Dialog
later.

This change makes sure that every `touchstart` event, we reset the
`overscroll-behavior` and `touch-action` properties so we can start
fresh.
Copy link

vercel bot commented Sep 19, 2025

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

Project Deployment Preview Comments Updated (UTC)
headlessui-react Ready Ready Preview Comment Sep 19, 2025 10:39am
headlessui-vue Ready Ready Preview Comment Sep 19, 2025 10:39am

}
d.group((_d) => {
d.addEventListener(doc, 'touchstart', (e) => {
_d.dispose()
Copy link
Member Author

Choose a reason for hiding this comment

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

If you look at this change with ?w=1 then the only real code that changed here is that we wrap everything in a d.group so we have a new set of disposables to work with.

Every time a touchstart event fires, we dispose the local set of disposables and start over.

This is a local set of disposables so we don't dispose any other styles that were set or event listeners that should still listen for events.

@RobinMalfait RobinMalfait merged commit 6b5709a into main Sep 19, 2025
8 checks passed
@RobinMalfait RobinMalfait deleted the fix/issue-3234 branch September 19, 2025 14:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scrolling with long content doesn't not work on iOS after outside click
2 participants