-
-
Notifications
You must be signed in to change notification settings - Fork 382
Description
Summary
The Drawer's swipe dismiss handler (useSwipeDismiss) prevents onClick from firing on non-interactive elements (e.g., <div onClick>) inside the drawer on touch devices, while native interactive elements like <button> work correctly.
Steps to reproduce
- Render a left-side Drawer with clickable
<div onClick>elements and<button onClick>elements inside the popup content - Open the drawer on a touch device (or mobile emulator)
- Tap a
<div onClick>element → onClick does not fire - Tap a
<button onClick>element → onClick fires correctly
Root cause
In useSwipeDismiss.ts, startSwipeAtPosition checks if the touch target matches DEFAULT_IGNORE_SELECTOR:
const DEFAULT_IGNORE_SELECTOR = 'button,a,input,select,textarea,label,[role="button"]';<button>matches → swipe tracking is skipped → normal click fires<div>does NOT match → swipe tracking starts (setSwiping(true)) → touch events are consumed →onClicknever synthesizes
Additional issue: data-swipe-ignore doesn't work on touch
The DrawerViewport has a data-swipe-ignore check in onPointerDown, but the handler exits early for event.pointerType === 'touch' (line ~682) before reaching that check. So the escape hatch exists but doesn't work for the input type where the bug occurs.
Prior art: Vaul
Vaul (which Base UI's drawer is inspired by) provides data-vaul-no-drag — a data attribute that opts elements out of swipe tracking for all input types including touch.
Suggested fix
One or more of:
- Make
data-swipe-ignorework for touch events — move the check before the touch early-return in theonPointerDownhandler, or add it to theonTouchStarthandler - Expose
ignoreSelectoras a configurable prop onDrawer.RootorDrawer.Viewportso users can extendDEFAULT_IGNORE_SELECTOR - Add a universal
data-no-swipeattribute (like Vaul'sdata-vaul-no-drag) that works for both pointer and touch events
Current workaround
Adding role="button" to clickable <div> elements makes them match [role="button"] in the ignore selector. This works and improves accessibility, but it's a workaround — users shouldn't need to know about internal selectors to make click handlers work inside a drawer.
Environment
@base-ui/reactversion:1.2.0- Browser: Mobile Safari / Chrome on iOS/Android
- Framework: React 19