CSS overscroll-behavior: Control Scroll Chaining and Bounce

CSS overscroll-behavior controls what happens when a user scrolls past the edge of a scroll container. It helps you stop unwanted scroll chaining, reduce bounce effects, and make nested scrolling feel more predictable.

Quick answer: Use overscroll-behavior to decide whether scrolling should pass from an inner scroller to the page, or stay contained at the scroll boundary. Choose contain or none when you want to keep scroll interactions inside a component.

Difficulty: Beginner

You'll understand this better if you know: how CSS overflow works, what a scroll container is, and the difference between the page viewport and an inner scrolling element.

1. What Is CSS overscroll-behavior?

overscroll-behavior is a CSS property that controls how a scroll container behaves when the user reaches its start or end. In other words, it tells the browser whether extra scrolling should stay inside that container or continue to another scrolling area.

Think of it as a boundary rule for scrolling. When the edge of a scrolling box is reached, the browser can either hand the gesture to another scroll area or keep it contained.

2. Why overscroll-behavior Matters

Nested scrolling is common in modals, sidebars, chat windows, and code panes. Without control, a user can reach the end of an inner panel and accidentally scroll the page behind it. That feels broken and makes interfaces harder to use.

overscroll-behavior matters because it lets you create cleaner, more predictable interactions without JavaScript. It is especially useful when a component should feel self-contained, such as a drawer, dialog, or feed inside a page layout.

It also improves control over browser-specific overscroll effects like rubber-band bounce on some platforms and pull-to-refresh behavior in certain browsers. The exact effect depends on the browser and device, but the intent is the same: keep scrolling where it belongs.

3. Basic Syntax or Core Idea

The property can be set on any scroll container. The shorthand controls both axes, and there are axis-specific longhands when you need different behavior horizontally and vertically.

Shorthand syntax

.scroller {
  overscroll-behavior: contain;
}

This example tells the browser to keep overscroll effects inside .scroller instead of letting them chain to the page or another ancestor.

Axis-specific syntax

.scroller {
  overscroll-behavior-y: contain;
  overscroll-behavior-x: auto;
}

Here, vertical overscroll is contained, but horizontal overscroll behaves normally. This is useful when only one direction should be isolated.

Common values

4. Step-by-Step Examples

Example 1: Prevent a modal from scrolling the page behind it

A modal with its own scroll area should usually keep the user inside the modal until they leave it intentionally. This is one of the most common use cases for overscroll-behavior.

.modal {
  max-height: 80vh;
  overflow: auto;
  overscroll-behavior: contain;
}

With this style, the modal scrolls normally, but reaching the top or bottom does not hand the scroll gesture to the page behind it.

Example 2: Stop a chat panel from chaining to the page

Chat panes and message lists often sit inside long pages. If a user reaches the end of the message list, you may not want the whole page to move.

.chat-list {
  height: 24rem;
  overflow-y: auto;
  overscroll-behavior-y: contain;
}

This keeps the conversation area in control of vertical scrolling and makes the page feel steadier.

Example 3: Disable bounce and pull-to-refresh style overscroll

Sometimes you want a fully locked scroll region, such as a canvas-like panel or a tightly controlled drawer. In that case, none is stricter than contain.

.drawer-panel {
  overflow: auto;
  overscroll-behavior: none;
}

This prevents the browser from showing many of the default boundary effects, which can make the panel feel more rigid and controlled.

Example 4: Control horizontal and vertical axes separately

Some layouts scroll in one direction but should not interfere with the page in another direction. Axis-specific values make that possible.

.carousel {
  display: flex;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  overscroll-behavior-y: auto;
}

This is useful when a horizontal scroller should not leak drag or wheel gestures into the rest of the page.

5. Practical Use Cases

These use cases all share the same goal: preserve the user's mental model of which area is currently being scrolled.

6. Common Mistakes

Mistake 1: Using overscroll-behavior on an element that is not scrollable

overscroll-behavior only matters when the element can actually scroll. If there is no overflow, the property has nothing to control.

Problem: The element has no scrollable overflow, so changing overscroll behavior will not visibly do anything.

.panel {
  overscroll-behavior: contain;
}

Fix: Make the element a real scroll container by giving it overflow and a constrained size.

.panel {
  max-height: 20rem;
  overflow: auto;
  overscroll-behavior: contain;
}

The corrected version works because the browser now has a scroll boundary to manage.

Mistake 2: Expecting contain to remove all overscroll effects

contain stops scroll chaining, but it does not always remove every local effect. On many browsers and devices, some bounce or boundary feedback can still appear.

Problem: The code prevents scroll chaining, but the developer expects it to fully disable boundary feedback everywhere.

.sheet {
  overflow: auto;
  overscroll-behavior: contain;
}

Fix: Use none when you want stricter suppression of overscroll effects.

.sheet {
  overflow: auto;
  overscroll-behavior: none;
}

The corrected version works because none applies a stronger boundary rule than contain.

Mistake 3: Applying only one axis when the problem happens in the other axis

If scrolling moves in both directions, setting only one axis can leave the real problem untouched.

Problem: Vertical chaining is still happening because only the horizontal axis was changed.

.list {
  overflow: auto;
  overscroll-behavior-x: contain;
}

Fix: Set the axis that matches the actual scroll direction, or use the shorthand if both axes should behave the same way.

.list {
  overflow: auto;
  overscroll-behavior-y: contain;
}

The corrected version works because the overscroll rule now matches the direction that actually causes the issue.

7. Best Practices

1. Use the least restrictive value that solves the problem

Start with contain when you only need to stop scroll chaining. It preserves more native behavior than none, which can make the interface feel more natural.

.modal {
  overflow: auto;
  overscroll-behavior: contain;
}

This approach keeps the scroll experience predictable without removing useful browser feedback unnecessarily.

2. Pair it with the correct overflow property

overscroll-behavior is not a replacement for overflow. The element still needs actual scrollable overflow to produce the boundary the property controls.

.sidebar {
  max-height: 100vh;
  overflow-y: auto;
  overscroll-behavior-y: contain;
}

Combining these properties makes the container behave as a real, controllable scroll region.

3. Target the smallest scope that needs the fix

Do not apply a global rule unless the whole page really needs it. Often a single component is the only place where scroll chaining causes trouble.

.dialog-content {
  overflow: auto;
  overscroll-behavior: contain;
}

This keeps the rest of the page responsive and avoids unexpected side effects in unrelated areas.

8. Limitations and Edge Cases

If your goal is to keep the background from scrolling while a dialog is open, you may need both proper dialog layout and a separate background-scroll strategy. overscroll-behavior handles boundary behavior, not full application state.

9. Practical Mini Project

Here is a small card layout with a scrollable comments panel. The goal is to keep the comments list contained so it does not drag the whole page when the user reaches the top or bottom.

.page {
  font-family: Arial, sans-serif;
  padding: 2rem;
}

.article-card {
  max-width: 36rem;
  border: 1px solid #ccc;
  border-radius: 0.75rem;
  padding: 1rem;
}

.comments {
  max-height: 12rem;
  overflow-y: auto;
  overscroll-behavior-y: contain;
  padding: 0.5rem;
  background: #f9f9f9;
}

.comment {
  padding: 0.5rem 0;
  border-bottom: 1px solid #e5e5e5;
}

This creates a contained comments area that scrolls independently from the page. The important part is the combination of a constrained height, overflow, and overscroll-behavior-y: contain.

10. Key Points

11. Practice Exercise

Build a settings panel with the following requirements:

Expected output: a contained settings panel that scrolls independently from the page without chaining vertical scroll gestures to the viewport.

Hint: Combine a fixed or limited height with overflow-y: auto and the vertical overscroll longhand.

Solution:

.settings-panel {
  max-height: 18rem;
  overflow-y: auto;
  overscroll-behavior-y: contain;
}

This solution works because the panel can scroll on its own, and the browser keeps the vertical scroll gesture inside that container at the boundary.

12. Final Summary

overscroll-behavior is a practical CSS property for controlling what happens when a user reaches the edge of a scrollable area. It is most useful in nested scrolling layouts, where you want to keep interaction inside a modal, drawer, feed, or panel instead of letting the page behind it move.

The main values are easy to remember: auto keeps the default browser behavior, contain stops scroll chaining, and none applies a stricter boundary rule. Pair the property with real overflow and the right axis, and you can solve many scrolling problems cleanly with CSS alone.

If you want to go further, study overflow, position: sticky, and scroll container behavior together. Those topics work closely with overscroll-behavior in real interfaces.