· 2 min read
overflow-x: auto quietly makes a vertical scroll container too
Three full-bleed carousels on a phone ate every upward swipe. The cause was one line of CSS that does something the property name does not say.

A visitor on a phone told me they got stuck. They would scroll down the page, reach a certain band, and then swiping up did nothing. The page held still under their thumb until they moved it somewhere else and tried again.
Three sections had the same symptom. All three were full-width horizontal lanes: a client roll, a certificate strip, and a row of article cards. All three used the same utility class.
.scroll-x {
overflow-x: auto;
overscroll-behavior-x: contain;
scrollbar-width: none;
}
That looks like it constrains one axis. It does not.
What the property actually does
The CSS overflow spec has a rule most people never hit: if you set one axis to something other than visible and leave the other alone, the other one does not stay visible. It computes to auto.
So every one of those lanes was a scroll container on both axes. Horizontally that was the point. Vertically it was invisible, because the content was the same height as the box.
Except it is never exactly the same height. Card padding, line-height rounding and fractional device pixels routinely leave a container with half a pixel of vertical scroll range. Half a pixel is not enough to see. It is more than enough for a browser to decide the element is scrollable and to route the whole gesture to it.
The result is a lane that consumes an entire upward swipe to travel half a pixel, while the page behind it stays exactly where it was.
Why it only showed up on a phone
On a desktop the wheel event bubbles once the element is at its scroll limit, so the page keeps moving and nobody notices. Touch does not work that way. The browser picks a scroll target when the gesture starts and commits to it for the life of that gesture. Pick wrong at touchstart and the user has to lift their finger and try again.
That is also why it was so hard to reproduce deliberately. It depends where the thumb lands.
The fix
.scroll-x {
overflow-x: auto;
overflow-y: hidden;
overscroll-behavior-x: contain;
}
One declaration. State the second axis instead of letting it be inferred.
What I would generalise from it
Any time you write overflow-x or overflow-y on its own, write the other one as well, even when it looks redundant. The value you get by default is not the value the property name implies, and the failure mode is silent on every machine a developer is likely to test on.
It is worth grepping for. I found three instances of the same class in one codebase, which means the bug shipped three times from a single line of CSS.