How touch-action: pan-y Handles Scroll and Swipes
The CSS touch-action: pan-y property allows web
applications to retain smooth native vertical page scrolling while
simultaneously capturing horizontal swipe gestures with JavaScript. By
explicitly declaring the allowed touch interactions directly in CSS, the
browser can immediately separate vertical scrolling from horizontal
movements without waiting for JavaScript execution, eliminating scroll
lag and gesture conflicts.
The Problem with Default Touch Handling
By default, mobile browsers handle all touch gestures directly on the viewport. When a user places a finger on the screen and drags, the browser must decide whether the gesture is meant to scroll the page, zoom, or interact with an element on the screen.
Before the touch-action property, developers handled
swipe gestures by listening to touchstart,
touchmove, and touchend events in JavaScript.
To prevent the page from moving horizontally or behaving unpredictably,
developers often called event.preventDefault(). However,
calling preventDefault() inside touch listeners forces the
browser to wait for the JavaScript thread to finish before deciding
whether to scroll, resulting in delayed, stuttering, or janky scrolling
behavior.
How
touch-action: pan-y Solves the Issue
The touch-action property informs the browser's
compositor thread about which touch interactions are allowed on a
specific element before any JavaScript touch event handlers are
executed.
When you apply touch-action: pan-y to an element:
- Vertical Panning Remains Native: The browser
retains full native control over vertical scrolling
(
pan-y). When a user swipes up or down, the browser immediately scrolls the viewport smoothly without consulting the main JavaScript thread. - Horizontal Panning Is Relinquished: The browser
disables its own default actions for horizontal movements
(
pan-x). The browser will not attempt to scroll horizontally or trigger native navigation gestures like swiping back or forward through browser history. - Gestures Pass Directly to JavaScript: Because the browser knows it will not handle horizontal panning natively, it immediately sends all horizontal pointer and touch events directly to your application logic.
Implementing
Horizontal Swipes with pan-y
This property is ideal for UI components like carousels, image sliders, tab switchers, and swipe-to-dismiss items.
.carousel-container {
touch-action: pan-y;
overflow: hidden;
}With this CSS rule applied, you can attach pointer event listeners in JavaScript to track horizontal movement:
const carousel = document.querySelector('.carousel-container');
let startX = 0;
carousel.addEventListener('pointerdown', (e) => {
startX = e.clientX;
});
carousel.addEventListener('pointerup', (e) => {
const diffX = e.clientX - startX;
if (diffX > 50) {
// Swipe right action
} else if (diffX < -50) {
// Swipe left action
}
});Performance and Usability Advantages
- Jank-Free Scrolling: The browser does not need to wait for event listeners to resolve before executing vertical scrolls, ensuring the user experiences standard 60fps or 120fps scrolling.
- Passive Event Listeners: Because you do not need to
call
event.preventDefault()to stop horizontal viewport shifting, you can safely use passive event listeners ({ passive: true }), which further improves rendering performance. - Predictable User Experience: Users can naturally scroll past interactive horizontal components while reading a page, but the moment their gesture aligns horizontally, the component responds instantly without accidental page shifting.