This article was translated from Japanese by AI and may contain inaccuracies. For the most accurate content, please refer to the original Japanese version.
かわいいエイのイラスト

Building UIs That Scroll Diagonally with CSS scroll-axis-lock

Browsers sometimes lock scrolling to a single axis, either vertical or horizontal. The scroll-axis-lock property lets you disable this axis locking. This article covers the basics of using scroll-axis-lock.

Have you ever tried to scroll a map or spreadsheet diagonally with a trackpad, only to find it moving straight up and down? Browsers infer the user's intent from the initial direction of a gesture and sometimes lock scrolling to a single axis, either vertical or horizontal. This behavior is called "axis locking."

Axis locking keeps content from drifting sideways unintentionally while you read down a page. On the other hand, it can get in the way of UIs like maps, where users want to move freely in both directions.

The CSS scroll-axis-lock property lets you disable axis locking on a per-scroll-container basis. This article walks through the basics of how to use it.

Note

As of September 22, 2026, scroll-axis-lock is available as a new feature in Chrome 153. The specification is an Editor's Draft of CSS Overflow Module Level 5.

What Is Scroll Axis Locking?

Even when you move your finger almost straight up or down, the gesture includes a small amount of horizontal movement. If the browser faithfully applied that horizontal movement to scrolling, the result could differ from what the user intended. So once the browser decides that the user "wants to scroll vertically," it may ignore the horizontal delta. This is called axis locking.

The specification gives an example in which a gesture produces 500px of vertical movement and 3px of horizontal movement. If the browser locks scrolling to the vertical axis, the 3px of horizontal movement is not reflected in the scroll.

This is convenient when reading down a page, but in UIs such as maps, spreadsheets, and canvases, where users want to move freely in both directions, axis locking can get in the way. When axis locking restricts the movement, users have to lift their finger and try again from a different angle.

scroll-axis-lock: none disables this process of locking input to a single axis. When input has both vertical and horizontal components, it prevents the horizontal component from being discarded by axis locking.

How to Use scroll-axis-lock

Specify scroll-axis-lock: none on the element that scrolls.

.table-container {
  width: 420px;
  height: 360px;
  overflow: auto;
  scroll-axis-lock: none;
}

overflow: auto creates a scrollable area when the content overflows. Adding scroll-axis-lock: none to that element disables the browser's axis locking. For the element to scroll in both directions, its content must overflow both vertically and horizontally.

The property accepts the following two values.

Value Meaning
auto The browser may lock scrolling to one axis. This is the initial value
none The browser must not lock scrolling to one axis

Note

auto does not force axis locking. Which gestures get locked depends on the browser and the input device.

Warning

Setting scroll-axis-lock: none on a parent element does not automatically apply it to child elements, because the property is not inherited. You need to specify it on the element that actually scrolls.

Why Do We Need a New Property?

The original proposal identifies the problem that axis locking prevents free movement in two-dimensional UIs such as maps and large diagrams.

The explainer describes an existing workaround: tracking the finger's movement in JavaScript and updating the scroll position manually. However, this approach requires you to handle the gestures yourself. And if the main thread that runs JavaScript is busy with other work, the response to user input can lag.

const container = document.querySelector(".table-container");
let previous = null;
 
container.addEventListener("touchstart", (event) => {
  const touch = event.touches[0];
  // Only handle single-finger gestures, recording the starting coordinates used to compute movement
  previous =
    event.touches.length === 1 ? { x: touch.clientX, y: touch.clientY } : null;
});
 
container.addEventListener(
  "touchmove",
  (event) => {
    if (!previous || event.touches.length !== 1) return;
    // Disable the browser's default scrolling and update the scroll position ourselves
    event.preventDefault();
 
    const touch = event.touches[0];
    container.scrollLeft += previous.x - touch.clientX;
    container.scrollTop += previous.y - touch.clientY;
    previous = { x: touch.clientX, y: touch.clientY };
  },
  { passive: false },
);
 
const reset = () => {
  previous = null;
};
container.addEventListener("touchend", reset);
container.addEventListener("touchcancel", reset);

To address the problems with these JavaScript workarounds, the scroll-axis-lock property was proposed as a standard CSS way to control axis-locking behavior.

Note

The initial proposal used the name and values overflow-axis-lock: proximity | none. In the CSSWG discussion in January 2026, the values were changed to auto, which leaves the default behavior up to the browser, and the name was changed to use the scroll- prefix to indicate that it controls scrolling.

Comparing the Behavior with a Large Table

Let's see how the behavior actually differs. The demo below places two identical tables side by side, with scroll-axis-lock: none applied to only one of them.

The easiest way to see the difference is to scroll vertically with a trackpad. The table without scroll-axis-lock: none scrolls straight down, while the table with scroll-axis-lock: none drifts slightly sideways as it scrolls.

Summary

  • scroll-axis-lock: none disables axis locking, which is when the browser locks scrolling to either the vertical or horizontal axis
  • The initial value auto leaves the decision to the browser and does not force axis locking
  • scroll-axis-lock is not inherited, so specify it on the element that actually scrolls

References

Comprehension check

Answer the following questions to deepen your understanding of the article.

What does the initial value auto of scroll-axis-lock mean?

  • It allows the browser to apply axis locking

    Correct!

    auto leaves axis locking up to the browser. Axis locking does not happen for every gesture.

  • It forces scrolling to lock to either the vertical or horizontal axis

    Try again

    auto does not force axis locking. The current specification defines no value that forces it.

  • It moves the scroll diagonally regardless of input

    Try again

    scroll-axis-lock does not transform the direction of input. auto allows axis locking.

  • It disables axis locking only for the vertical axis

    Try again

    auto does not disable locking for a specific axis. The value that disables axis locking is none.