overscroll-behavior: Stop the Page Scrolling Behind Your Modal
Scroll a chat panel to its end and keep scrolling: the page behind it starts moving. Same with dropdowns, sidebars, code blocks. That hand-off is "scroll chaining" - the browser helpfully forwarding leftover scroll to the parent - and in overlays it's almost never what anyone wants.
.modal-body,
.dropdown-list,
.chat-panel {
overflow-y: auto;
overscroll-behavior-y: contain;
}
contain keeps the scroll inside the container: hitting the end just stops. The elastic bounce (on platforms that have it) still plays within the element, but nothing leaks to the page. This replaces the fragile JS approaches - wheel-event preventDefault math, or toggling body overflow (which causes its own layout jump).
The values
- auto - default chaining.
- contain - no chaining, local overscroll effects stay. The right choice for 95% of cases.
- none - no chaining and no local bounce/glow. For custom scroll experiences where even the rubber-band would look wrong.
The body-level tricks
/* Kill pull-to-refresh on a web app (mobile Chrome) */
body {
overscroll-behavior-y: contain;
}
Pull-to-refresh is an overscroll effect, so containing it at body level disables it - essential for PWAs with their own gesture handling, canvas drawing apps, and games. Same property also stops the Chrome two-finger-swipe navigation glow on the x axis (overscroll-behavior-x: contain) for horizontal carousels near the viewport edge.
What it does not do
Honest limits: contain stops scroll chaining, but if the overlay itself doesn't scroll (a short menu), there's no scroll to contain and wheel events still reach the page in some engines - fully scroll-locked modals should still set overflow: hidden on body (with scrollbar-gutter handling the shift) or use the dialog element, which manages this properly. And keyboard scrolling (space, PageDown) follows focus, not hover - a user can always scroll what's focused. contain is a UX seatbelt, not an access-control mechanism.
Support: Chromium and Firefox for many years, Safari 16+. At one declaration per scrollable overlay, it's the cheapest scroll-behavior fix in CSS - add it to your component library's panel styles once and forget the problem existed.
Full-stack web developer sharing practical tutorials and building tools that ship.
Got something on your mind?
My inbox is open - no forms disappearing into the void here.
- Just say hello Found a tutorial useful? Spotted a mistake? Tell me.
- Hire me for a project Have something custom in mind? Let's talk scope and timelines.
- Product support Bought something here? I'll help you get it running.
I usually reply within 1-2 business days.