Overscroll Behavior
The Problem
When a user scrolls to the end of a nested scrollable area — a modal, sidebar, dropdown, or chat panel — the browser "chains" the scroll event to the nearest scrollable ancestor. The background page suddenly starts scrolling beneath the overlay. This is called scroll chaining, and it is one of the most common UX bugs in web applications.
Before overscroll-behavior, preventing this required JavaScript scroll-locking hacks: listening for wheel events, calculating scroll positions, and calling preventDefault() at the right moment. These solutions were fragile, caused jank, and often broke touch device scrolling entirely. On mobile, overscroll effects like pull-to-refresh could also trigger unexpectedly inside custom scroll areas.
The Solution
The overscroll-behavior property gives you single-line CSS control over what happens when a scroll container reaches its boundary.
auto(default): Normal behavior — scroll chains to the parent and native overscroll effects (bounce, pull-to-refresh) are active.contain: Prevents scroll chaining to the parent, but native overscroll effects (like the rubber-band bounce on iOS/macOS) still apply within the element itself.none: Prevents both scroll chaining and all native overscroll effects. The scroll simply stops.
You can also control each axis independently with overscroll-behavior-x and overscroll-behavior-y.
Core Principles
auto (Default)
The default value. Scrolling chains to the parent when the element reaches its scroll boundary. This is usually fine for the main page content but causes problems in overlays, modals, and sidebars.
contain
The most commonly needed value. It prevents scroll chaining so that scrolling stays trapped inside the element. Use this on any independently scrollable region — modals, sidebars, chat panels, dropdown menus — where you do not want the background to scroll.
none
Goes further than contain by also suppressing native overscroll effects like the rubber-band bounce or pull-to-refresh. Use this when you want scrolling to stop completely at the boundary with no visual feedback. Useful for embedded app-like interfaces.
Code Examples
Preventing Scroll Chaining on a Modal
.modal-body {
max-height: 80vh;
overflow-y: auto;
overscroll-behavior-y: contain;
}
This single line prevents the background page from scrolling when the user scrolls to the end of the modal content.
Preventing Accidental Browser Back Navigation
.horizontal-scroller {
overflow-x: auto;
overscroll-behavior-x: contain;
}
On some browsers, a horizontal overscroll gesture triggers browser back/forward navigation. Setting overscroll-behavior-x: contain on horizontal scroll areas prevents this accidental navigation.
App Shell with Contained Panels
.app-shell {
display: grid;
grid-template-columns: 250px 1fr 300px;
height: 100vh;
}
.sidebar-nav {
overflow-y: auto;
overscroll-behavior: contain;
}
.main-content {
overflow-y: auto;
}
.detail-panel {
overflow-y: auto;
overscroll-behavior: contain;
}
In a multi-panel layout, apply overscroll-behavior: contain to each secondary scrollable panel so they do not chain scroll into the main content area.
Disabling Pull-to-Refresh
body {
overscroll-behavior-y: none;
}
On mobile browsers, pulling down at the top of the page triggers a refresh. Setting overscroll-behavior-y: none on the body disables this behavior — useful for web apps that have their own refresh mechanism.
Common AI Mistakes
- Not suggesting
overscroll-behaviorat all: Defaulting to JavaScript scroll-locking libraries orevent.preventDefault()hacks when a single CSS property solves the problem. - Applying it to the wrong element:
overscroll-behaviormust be set on the element that has the scroll (overflow: autooroverflow: scroll), not on a parent or wrapper. - Always using
noneinstead ofcontain: Usingnonesuppresses all overscroll feedback, which can feel unnatural. Prefercontainunless you specifically need to suppress bounce effects. - Forgetting the axis-specific variants: Using
overscroll-behavior: containwhen only one axis needs containment, likeoverscroll-behavior-x: containfor a horizontal scroller. - Not combining with
overflow:overscroll-behavioronly takes effect on scroll containers. If the element does not haveoverflow: autooroverflow: scroll, the property has no effect.
When to Use
- Modals and dialogs: Prevent background page scroll when scrolling inside a modal.
- Sidebars and navigation panels: Keep sidebar scroll independent from the main content.
- Chat and messaging panels: Prevent the page from scrolling when users scroll through message history.
- Dropdown menus: Long dropdowns should not scroll the page behind them.
- Horizontal scroll areas: Prevent accidental browser back/forward navigation with
overscroll-behavior-x: contain. - Mobile web apps: Disable pull-to-refresh with
overscroll-behavior-y: noneon the body when the app handles its own refresh logic.