CSS overscroll-behavior Explained
overscroll-behavior stops scroll chaining and pull-to-refresh bounce at a container's edge. How the property works and when to use it.
The overscroll-behavior CSS property controls what happens when a user scrolls past the edge of a scrollable element — specifically, whether that extra scroll “leaks” out to the parent container or the page underneath. It’s the fix for the classic annoyance of scrolling to the bottom of a chat panel or modal and having the whole page start scrolling with it.
The problem: scroll chaining
By default, browsers implement scroll chaining: when a nested scrollable element (a div with overflow: auto, say) reaches its scroll boundary, any further scroll input is passed up to the next scrollable ancestor. Scroll to the bottom of a sidebar list, keep scrolling, and the main page content starts moving instead. On touch devices this is compounded by overscroll effects — the rubber-band bounce on iOS or the pull-to-refresh gesture on Android — which can trigger even when the user only meant to scroll within a nested box.
Both behaviors make sense as defaults for a single scrollable page, but they actively work against you in layouts with nested scroll regions: chat windows, modals, dropdown menus, code panels, carousels.
Syntax and values
.chat-panel {
overflow-y: auto;
overscroll-behavior: contain;
}
auto— the default. Scroll chains to the parent and overscroll effects (bounce, pull-to-refresh) apply normally.contain— scrolling stops at the element’s boundary. The parent doesn’t scroll, but the element itself may still show a local bounce/glow effect at its own edge.none— same ascontain, but also suppresses the element’s own boundary overscroll effect entirely.
You can also set it per axis with overscroll-behavior-x and overscroll-behavior-y, which matters for anything that scrolls horizontally, like a carousel or a code block with long lines.
A practical example
A common source of user complaints: a modal dialog with scrollable content, opened over a long page. Without overscroll-behavior, scrolling to the end of the modal’s content and continuing to scroll moves the page behind it — the modal appears to let the background “escape.”
.modal-body {
overflow-y: auto;
overscroll-behavior-y: contain;
}
Setting contain on the modal body keeps scroll input inside the modal once it reaches either boundary. This pairs naturally with the HTML <dialog> element, which already traps focus and blocks interaction with the rest of the page — overscroll-behavior closes the remaining gap by trapping scroll too.
The same technique applies to chat interfaces, comment threads, and any fixed-height panel with its own scrollbar. It’s also worth setting on html or body with none if you’re building a full-screen app (a game, a map, a drawing tool) where you want to disable the browser’s pull-to-refresh gesture entirely.
overscroll-behavior vs touch-action
It’s easy to confuse overscroll-behavior with touch-action, but they solve different problems:
overscroll-behavior | touch-action | |
|---|---|---|
| Controls | What happens at a scroll boundary | Which gestures a browser handles natively at all |
| Typical use | Stop scroll chaining, disable bounce/pull-to-refresh | Disable pinch-zoom, pan, or double-tap-to-zoom on an element |
| Affects mouse wheel | Yes | No, touch/pointer gestures only |
| Common value | contain | none, pan-y, manipulation |
touch-action governs whether the browser intercepts a gesture for its own default behavior (zooming, panning) versus handing it to your JavaScript. overscroll-behavior governs where scroll input goes once it’s already scrolling a box. They’re often used together on custom drag-and-drop or gesture-based UI, but neither substitutes for the other.
Browser support and fallbacks
overscroll-behavior is supported in all modern evergreen browsers. There’s no meaningful fallback needed — in browsers that don’t recognize the property, scroll chaining just behaves as it always did, which is a safe default degradation rather than a broken one. This makes it a low-risk property to add: it’s pure enhancement, similar in spirit to scroll-snap or container queries, where unsupported browsers simply ignore the rule rather than failing.
One caveat: overscroll-behavior: contain on an element only stops chaining to that element’s scroll ancestors. It doesn’t stop chaining if you also want to prevent, say, a nested scrollable element three levels down from chaining past its immediate parent to the grandparent — each boundary needs its own declaration if you want scroll fully contained at every level.
The takeaway
overscroll-behavior gives you control over scroll chaining and overscroll effects without JavaScript. Use contain on modals, chat panels, and other nested scroll regions to stop scroll from leaking into the page behind them; use none on full-screen apps to kill pull-to-refresh and rubber-banding outright. It’s a one-line, zero-risk addition to any layout with nested scrollable containers.
Tagged
Keep reading
Takina · · 4 min read What Is CSS Subgrid? Nested Grid Alignment Explained
CSS subgrid lets a nested grid item inherit its parent's track sizing, so children can align to the same columns or rows across unrelated containers.
Takina · · 4 min read CSS field-sizing Property Explained
The CSS field-sizing property lets form controls like textareas grow to fit their content automatically, without JavaScript resize listeners.
Takina · · 3 min read CSS Scrollbar Styling: scrollbar-color and scrollbar-width
CSS now styles scrollbars natively with scrollbar-color and scrollbar-width, replacing years of vendor-prefixed hacks. Here's how they work.