This proposal introduces the scroll-axis-lock CSS property as a solution to letting authors
opt-out of default scroll axis locking behavior when desired.
- Blink Interactions
- GitHub Issue: w3c/csswg-drafts#13207
- Spec Pull Request: w3c/csswg-drafts#14152
- Introduction
- Use cases
- Current workarounds
- Proposed Solution
- Considered alternatives
- Security and Privacy Considerations
- Accessibility Considerations
- Stakeholder Feedback / Opposition
- References & acknowledgements
Web browsers often "lock" a user's scrolling gesture to a single axis when that gesture starts with significantly more movement in one axis than in the perpendicular axis. In many cases, this behavior improves the user's experience by avoiding accidental scrolls along the perpendicular axis when the user's intent was to scroll only one axis. However, in cases where an author wishes their element to always be diagonally scrollable, this behavior forces the user to either start their gesture at an angle that doesn't trigger the locking or restart their gesture if locking has already kicked in.
This project addresses this issue by introducing a CSS property, scroll-axis-lock, which lets the website author indicate that the web browser should not apply locking to user gesture scrolls on an element.
Web applications that feature 2D panning such as interactive maps, large architectural diagrams, schematics, or infinite canvas design tools (e.g., collaborative whiteboards) rely on freeform diagonal movement. When using native scroll containers for these features, the browser's axis-locking heuristic might constrain the user's ability to freely scroll diagonally.
Modern mobile applications frequently use gestures where a user can swipe a photo or card vertically to dismiss it, while still allowing some natural horizontal movement. In these cases, the author's intent is for the swiped-away element to be translated away in a manner that closely follows the user's gesture, without being locked strictly to a vertical or horizontal track.
To achieve the 2D panning and gesture effects described above without native browser support for unlocking scroll axes, developers are forced to bypass native scrolling entirely and implement custom gesture handlers in JavaScript.
This typically involves the following steps:
- Setting
touch-action: none(or preventing default ontouchstart/touchmoveevents) on the container to disable native browser scroll behavior, thereby avoiding the automatic axis-locking heuristic. - Tracking touch coordinate deltas (
clientX/clientY) or wheel events in JavaScript manually. - Applying these deltas manually to the element using CSS transforms (
translate3d) or programmatically updating thescrollTopandscrollLeftoffsets.
const element = document.querySelector('.pan-element');
let startX = 0, startY = 0;
element.addEventListener('touchstart', (e) => {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
});
element.addEventListener('touchmove', (e) => {
// Prevent default native scroll and axis-locking
e.preventDefault();
const dx = e.touches[0].clientX - startX;
const dy = e.touches[0].clientY - startY;
// Apply manual translation
element.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
}, { passive: false });-
Performance Overhead: Because JavaScript gesture handlers run on the browser's main thread, any heavy background script execution can block the gesture, leading to stuttering (jank). Additionally, processing gesture logic in JavaScript introduces input latency compared to native scrolling, which is often handled directly by optimized, dedicated browser threads.
-
Developer Overhead and Side-Effects: Re-implementing scrolling behavior in JavaScript requires developers to write custom coordinate tracking logic, manually manage gesture state, and coordinate touch interactions with other CSS layouts or animations on the page. If implemented by modifying CSS
transformrules rather than native scrolling, the developer might have to consider the implications of how this style modification interacts with other scroll-dependent platform features, such as scroll events, scroll snapping, Intersection Observers, or scroll-driven animations.
We propose introducing the scroll-axis-lock CSS property. This property allows developers to explicitly opt-out of axis locking on scroll containers.
- Name:
scroll-axis-lock - Value:
auto|none - Initial:
auto - Applies to: Scroll containers (or all elements, acting on scroll containers)
- Inherited: No
- Computed value: Specified keyword
- Animation Type: Discrete
.pan-container {
overflow: auto;
scroll-axis-lock: none; /* Disable scroll axis locking */
}auto: The user agent may apply its default scroll axis locking heuristic to gestures on this element.none: The user agent must not lock scrolls on the element to a single axis, allowing free 2D scrolling.
By setting scroll-axis-lock: none on a container, the developer ensures that any gesture starting on that element will scroll in both X and Y dimensions simultaneously, preserving the exact diagonal vector of the user's input from the very beginning of the gesture.
<div class="map-viewport">
<div class="map-content">...</div>
</div>
<style>
.map-viewport {
width: 100vw;
height: 100vh;
overflow: auto;
/* Ensure diagonal panning works immediately on touch/trackpad */
scroll-axis-lock: none;
}
.map-content {
width: 5000px;
height: 5000px;
}
</style>We considered whether we could leverage the touch-action property to solve this, either by overloading existing values (like pan-x pan-y) or adding a new keyword (such as pan-free). However, we decided against this for two reasons:
-
Semantic Mismatch (Gesture Filtering vs. Scroll Heuristics): The
touch-actionproperty is designed to filter whether the browser should intercept a touchscreen gesture. Once the browser begins executing a panning gesture, the scrolling heuristics (such as axis locking) are governed by the scrolling engine (CSS Overflow), not by input event filtering. Conflating these two stages of input handling would overload the purpose oftouch-action. -
Input Modality Constraints (Direct Touch vs. Trackpad/Wheel): The
touch-actionproperty only governs direct touchscreen interactions. It has no effect on mouse wheel or trackpad scrolling (which rely on scroll/wheel events).
By defining scroll-axis-lock as a CSS Overflow property, we ensure it applies consistently across all input modalities (touchscreen, trackpad, and mouse wheel).
We considered making scroll-axis-lock an inherited property, or having it propagate from the body or html elements to the viewport (similar to how overflow propagates).
However, we decided against inheritance because different scrollable components inside a page may require different locking behaviors (e.g., an article text container using auto inside a larger map application using none).
The property was initially proposed as overflow-axis-lock (aligned with the overflow property). However, the CSS Working Group resolved to use the scroll- prefix (specifically scroll-axis-lock) because this property describes the scrolling behavior and gesture handling rather than the layout/clipping behavior defined by overflow. This is also more consistent with properties like scroll-snap-type and scroll-behavior.
This property does not expose any new hardware sensors, user identity, or cross-origin information. It only alters the threshold heuristics used to trigger scrolling on a container based on pointer inputs the page is already authorized to receive. The feature respects standard same-origin boundaries and does not leak scroll state or content across origins.
This property affects how the browser responds to scrolling gestures, which can influence a user's overall scrolling experience.
On 2D panning areas like maps and large canvases or images, the browser's default axis locking can introduce some friction into the scrolling experience. When this happens, it is often not the behavior the website author intended, and it adds a small burden on the user to overcome that locking friction to move diagonally.
Setting scroll-axis-lock: none helps by letting diagonal movement work smoothly from the start of the gesture, removing this extra friction.
Default axis locking is a helpful feature for users who find it difficult to swipe in a straight line, as it keeps the content from sliding sideways while they are reading unidirectional feeds or articles.
To ensure we don't accidentally disrupt this:
- Opt-in by default: The property defaults to
auto, so existing pages will continue to use the browser's stabilizing axis lock by default. - Limited impact on unidirectional content: Even if a developer sets
scroll-axis-lock: noneon a unidirectional scroll container (one that only overflows vertically or horizontally), it is unlikely to degrade the experience. Since there is no content overflowing in the perpendicular direction, the browser cannot scroll that way, preventing drift.
See CSS Working Group Issue 13207 for
- Working Group Resolution
- Supporting Comment.
Many thanks to the CSS Working Group members for their valuable feedback, in particular:
- Robert Flack (flackr) for proposing and driving the initial issue.