Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Explainer for scroll-axis-lock

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.

Proponents

  • Blink Interactions

Participate

Table of Contents


Introduction

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.

Use cases

2D Panning Interfaces (Maps, Diagrams, and Infinite Canvases)

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.

Swipe-to-Dismiss in 2D (Mobile-like Gestures)

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.

Current workarounds

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:

  1. Setting touch-action: none (or preventing default on touchstart/touchmove events) on the container to disable native browser scroll behavior, thereby avoiding the automatic axis-locking heuristic.
  2. Tracking touch coordinate deltas (clientX/clientY) or wheel events in JavaScript manually.
  3. Applying these deltas manually to the element using CSS transforms (translate3d) or programmatically updating the scrollTop and scrollLeft offsets.

Code Example: JavaScript Workaround

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 });

Why this is problematic

  • 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 transform rules 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.


Proposed Solution

We propose introducing the scroll-axis-lock CSS property. This property allows developers to explicitly opt-out of axis locking on scroll containers.

The scroll-axis-lock property

  • 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

Syntax

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

How it solves the use cases

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.

Example: 2D Map Container

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

Considered alternatives

Using touch-action to disable axis locking

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-action property 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 of touch-action.

  • Input Modality Constraints (Direct Touch vs. Trackpad/Wheel): The touch-action property 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).

Making scroll-axis-lock inherited or viewport-propagated

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).

Alternative Property Names (overflow-axis-lock)

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.


Security and Privacy Considerations

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.


Accessibility Considerations

This property affects how the browser responds to scrolling gestures, which can influence a user's overall scrolling experience.

Smoother 2D Navigation

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.

Risks and Mitigations

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: none on 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.

Stakeholder Feedback / Opposition

See CSS Working Group Issue 13207 for


References & acknowledgements

Many thanks to the CSS Working Group members for their valuable feedback, in particular:

  • Robert Flack (flackr) for proposing and driving the initial issue.

About

Explainer for CSS property: scroll-axis-lock

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages