Skip to content

Conversation

singingwolfboy
Copy link

Some components require scroll-locking for accessibility purposes; however, it seems that on iOS, this scroll locking was going one step too far, and preventing users from being able to use the pinch-to-zoom gesture. Pinch-to-zoom is important, especially for people with low vision conditions; zooming in helps them read and understand the web page.

On iOS, we we are using the touch-action CSS property to disable scrolling around the page. However, instead of setting this property to none to disable all touch interaction, we can set it to pinch-zoom, which should disable all scrolling touch interactions, but still allow the user to zoom in and out as necessary.

I discovered this accessibility issue when trying to use a <Dialog> component from Headless UI. The dialog showed an image, and I wanted to zoom in on the image in order to view it in greater detail, but I discovered that I could not. This change fixes that problem.

Copy link

vercel bot commented Jan 1, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
headlessui-react ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 1, 2025 11:41am
headlessui-vue ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 1, 2025 11:41am

@Merott
Copy link

Merott commented Apr 14, 2025

I need this too.

The fix appears to be more involved than this, though.

Specifically, it seems like we still need to detect a pinch gesture with JavaScript and return early from the touchmove handler if we do.

See these changes.

@RobinMalfait
Copy link
Member

Hey! Thanks for the PR. I solved this in another way in #3801 where we weren't properly resetting the touch-action. The touch-action was only set on elements outside the dialog and not the dialog itself.

I included 2 videos in the PR to see the behavior differences.

This should be enough to fix this issue. It will be available in the next release.

You can already try it using:

  • npm install @headlessui/react@insiders.

pull bot pushed a commit to TheRakeshPurohit/headlessui that referenced this pull request Sep 19, 2025
…#3801)

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 tailwindlabs#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



https://github.com/user-attachments/assets/d79d9794-3732-4201-a4c9-43ea36d302ce

**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


https://github.com/user-attachments/assets/f6c1c765-3c8c-4d3c-91c2-1b43507f6434


Fixes: tailwindlabs#3234 
Closes: tailwindlabs#3602
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants