CSSoverscroll-behavior

overscroll-behavior

overscroll-behavior controls what happens when a user keeps scrolling past the boundary of a scrollable area — the moment they hit the top or bottom of a box and keep dragging, swiping, or turning the wheel. By default, the browser lets that extra scroll "chain" into the next scrollable ancestor, usually the page itself. That default is the cause of one of the most common, genuinely annoying scroll bugs on the web.
The problem: scroll chaining

Picture a modal dialog with its own internally scrolling content. The user scrolls down inside the modal to read it. Once they reach the bottom of the modal's content, the browser doesn't stop — it keeps interpreting further scroll input as scrolling the page underneath the modal, which the user usually can't even see. The page behind the modal silently scrolls out from under them. This is scroll chaining, and it's the default behavior everywhere unless you turn it off.

The fix: overscroll-behavior: contain
Setting overscroll-behavior: contain on the scrollable element stops scroll chaining at its boundary — once the user reaches the end of that element's own content, further scroll input simply does nothing instead of propagating to the parent:

CSS
.modal__body {
  overflow-y: auto;
  max-height: 70vh;
  overscroll-behavior: contain;
}
Worked example: a scrollable modal

HTML
<div class="modal-overlay">
  <div class="modal">
    <h2>Terms of Service</h2>
    <div class="modal__body">
      <!-- long scrollable content -->
    </div>
  </div>
</div>

CSS
body {
  overflow: hidden; /* page shouldn't scroll while modal is open anyway */
}

.modal__body {
  overflow-y: auto;
  max-height: 70vh;
  overscroll-behavior: contain; /* stops chaining even if body scroll leaks */
}
With contain in place, scrolling to the end of the modal's content — in either direction — simply stops there. The page behind it never moves, and on touch devices the characteristic elastic "bounce" also stays local to the modal instead of bouncing the whole page.
The three values

Value

Effect

auto

Default — overscroll chains to the parent scrollable area (and eventually the page)

contain

Stops chaining to the parent, but still shows the browser's own overscroll effects (e.g. bounce/glow) locally

none

Stops chaining AND suppresses the browser's native overscroll effects entirely

It can also be set per-axis with overscroll-behavior-x and overscroll-behavior-y — useful for a horizontally scrolling carousel that shouldn't chain sideways but is fine chaining vertically.
contain doesn't stop scrolling — it only stops chaining to the parent
The element itself still scrolls normally right up to its boundary. overscroll-behavior only changes what happens once you run out of content to scroll — it's not a way to disable scrolling, and it has no effect on an element that isn't scrollable in the first place.
A common real default: contain the whole page
Many sites set overscroll-behavior-y: contain (or none) on html or body to kill the "pull to refresh" and rubber-band bounce effect at the very top/bottom of the page on mobile browsers, independent of any modal use case.
Next
See how overflow itself can be clipped without even creating a scroll container: overflow: clip vs hidden vs scroll.