Back to Blog
CSSoverscroll-behaviorScrollingUX

CSS overscroll-behavior Property Guide

Master the CSS overscroll-behavior property for controlling scroll chaining and bounce effects.

B
Bootspring Team
Engineering
December 1, 2019
5 min read

The overscroll-behavior property controls what happens when a scroll boundary is reached. Here's how to use it.

Basic Usage#

1/* Prevent scroll chaining */ 2.modal { 3 overscroll-behavior: contain; 4} 5 6/* Values */ 7.auto { 8 overscroll-behavior: auto; /* Default browser behavior */ 9} 10 11.contain { 12 overscroll-behavior: contain; /* Prevent scroll chaining */ 13} 14 15.none { 16 overscroll-behavior: none; /* No scroll chaining, no bounce */ 17} 18 19/* Individual axes */ 20.horizontal { 21 overscroll-behavior-x: contain; 22 overscroll-behavior-y: auto; 23} 24 25.vertical { 26 overscroll-behavior-x: auto; 27 overscroll-behavior-y: contain; 28}

Preventing Scroll Chaining#

1/* Modal that doesn't scroll parent */ 2.modal-overlay { 3 position: fixed; 4 inset: 0; 5 background: rgba(0, 0, 0, 0.5); 6 overflow: auto; 7} 8 9.modal-content { 10 max-height: 80vh; 11 overflow-y: auto; 12 overscroll-behavior: contain; 13} 14 15/* Sidebar scroll isolation */ 16.sidebar { 17 height: 100vh; 18 overflow-y: auto; 19 overscroll-behavior-y: contain; 20} 21 22/* Chat message list */ 23.message-list { 24 flex: 1; 25 overflow-y: auto; 26 overscroll-behavior: contain; 27} 28 29/* Dropdown menu */ 30.dropdown-menu { 31 max-height: 300px; 32 overflow-y: auto; 33 overscroll-behavior: contain; 34}

Pull-to-Refresh Control#

1/* Disable pull-to-refresh on mobile */ 2body { 3 overscroll-behavior-y: contain; 4} 5 6/* Allow on specific areas */ 7.refreshable-content { 8 overscroll-behavior-y: auto; 9} 10 11/* Prevent refresh but allow bounce */ 12.no-refresh { 13 overscroll-behavior-y: contain; 14} 15 16/* No bounce at all */ 17.no-bounce { 18 overscroll-behavior-y: none; 19} 20 21/* Custom pull-to-refresh container */ 22.ptr-container { 23 overscroll-behavior-y: contain; 24 /* Implement custom PTR with JS */ 25}
1/* Prevent back/forward swipe gestures */ 2body { 3 overscroll-behavior-x: contain; 4} 5 6/* Carousel that doesn't trigger navigation */ 7.carousel { 8 display: flex; 9 overflow-x: auto; 10 overscroll-behavior-x: contain; 11 scroll-snap-type: x mandatory; 12} 13 14.carousel-item { 15 flex: 0 0 100%; 16 scroll-snap-align: start; 17} 18 19/* Horizontal scrolling section */ 20.horizontal-scroll { 21 overflow-x: auto; 22 overscroll-behavior-x: contain; 23 -webkit-overflow-scrolling: touch; 24} 25 26/* Image gallery */ 27.gallery { 28 display: flex; 29 overflow-x: auto; 30 overscroll-behavior-x: contain; 31 gap: 1rem; 32 padding: 1rem; 33}

Nested Scroll Containers#

1/* Outer container scrolls normally */ 2.outer-scroll { 3 height: 100vh; 4 overflow-y: auto; 5} 6 7/* Inner container doesn't chain */ 8.inner-scroll { 9 height: 300px; 10 overflow-y: auto; 11 overscroll-behavior: contain; 12} 13 14/* Multiple nested levels */ 15.level-1 { 16 overflow: auto; 17} 18 19.level-2 { 20 overflow: auto; 21 overscroll-behavior: contain; 22} 23 24.level-3 { 25 overflow: auto; 26 overscroll-behavior: contain; 27} 28 29/* Card with scrollable content */ 30.card { 31 max-height: 400px; 32 overflow: hidden; 33} 34 35.card-body { 36 max-height: 300px; 37 overflow-y: auto; 38 overscroll-behavior-y: contain; 39}

Mobile App Feel#

1/* App-like scrolling */ 2html, body { 3 height: 100%; 4 overflow: hidden; 5} 6 7.app-container { 8 height: 100%; 9 display: flex; 10 flex-direction: column; 11} 12 13.app-header { 14 flex-shrink: 0; 15} 16 17.app-content { 18 flex: 1; 19 overflow-y: auto; 20 overscroll-behavior-y: contain; 21 -webkit-overflow-scrolling: touch; 22} 23 24.app-footer { 25 flex-shrink: 0; 26} 27 28/* Prevent rubber-banding on iOS */ 29.ios-fix { 30 position: fixed; 31 width: 100%; 32 height: 100%; 33 overflow: hidden; 34} 35 36.ios-fix .scroll-content { 37 height: 100%; 38 overflow-y: auto; 39 overscroll-behavior-y: none; 40}

Drawer and Sheet Components#

1/* Bottom sheet */ 2.bottom-sheet { 3 position: fixed; 4 bottom: 0; 5 left: 0; 6 right: 0; 7 max-height: 80vh; 8 overflow-y: auto; 9 overscroll-behavior-y: contain; 10 border-radius: 16px 16px 0 0; 11 background: white; 12} 13 14/* Side drawer */ 15.drawer { 16 position: fixed; 17 top: 0; 18 left: 0; 19 width: 280px; 20 height: 100vh; 21 overflow-y: auto; 22 overscroll-behavior-y: contain; 23 background: white; 24} 25 26/* Slide-up panel */ 27.slide-panel { 28 position: fixed; 29 bottom: 0; 30 left: 0; 31 right: 0; 32 height: 60vh; 33 overflow-y: auto; 34 overscroll-behavior: contain; 35}

Form Containers#

1/* Long form in modal */ 2.form-modal { 3 max-height: 90vh; 4 overflow-y: auto; 5 overscroll-behavior: contain; 6} 7 8/* Scrollable fieldset */ 9.fieldset-scroll { 10 max-height: 200px; 11 overflow-y: auto; 12 overscroll-behavior: contain; 13 padding: 1rem; 14 border: 1px solid #ddd; 15} 16 17/* Autocomplete dropdown */ 18.autocomplete-list { 19 max-height: 250px; 20 overflow-y: auto; 21 overscroll-behavior: contain; 22 border: 1px solid #ddd; 23 border-top: none; 24} 25 26/* Multi-select options */ 27.select-options { 28 max-height: 200px; 29 overflow-y: auto; 30 overscroll-behavior: contain; 31}

Data Tables#

1/* Scrollable table wrapper */ 2.table-wrapper { 3 max-height: 500px; 4 overflow: auto; 5 overscroll-behavior: contain; 6} 7 8/* Horizontal scroll table */ 9.table-horizontal { 10 overflow-x: auto; 11 overscroll-behavior-x: contain; 12} 13 14/* Both directions */ 15.table-both { 16 overflow: auto; 17 overscroll-behavior: contain; 18} 19 20/* Fixed header table */ 21.fixed-header-table { 22 position: relative; 23} 24 25.fixed-header-table thead { 26 position: sticky; 27 top: 0; 28 z-index: 1; 29} 30 31.fixed-header-table tbody { 32 overflow-y: auto; 33 overscroll-behavior-y: contain; 34}

Code Editor Pattern#

1/* Code editor scroll */ 2.code-editor { 3 display: flex; 4 height: 400px; 5} 6 7.line-numbers { 8 flex-shrink: 0; 9 overflow: hidden; 10} 11 12.code-content { 13 flex: 1; 14 overflow: auto; 15 overscroll-behavior: contain; 16} 17 18/* Terminal output */ 19.terminal { 20 height: 300px; 21 overflow-y: auto; 22 overscroll-behavior-y: contain; 23 background: #1e1e1e; 24 color: #d4d4d4; 25 font-family: monospace; 26} 27 28/* Log viewer */ 29.log-viewer { 30 height: 100%; 31 overflow-y: auto; 32 overscroll-behavior: contain; 33}

JavaScript Integration#

1// Check support 2const supportsOverscroll = CSS.supports('overscroll-behavior', 'contain'); 3 4// Apply dynamically 5function preventScrollChain(element) { 6 element.style.overscrollBehavior = 'contain'; 7} 8 9// Toggle based on state 10function setModalOpen(isOpen) { 11 document.body.style.overflow = isOpen ? 'hidden' : ''; 12 modal.style.overscrollBehavior = isOpen ? 'contain' : 'auto'; 13} 14 15// Custom scroll handling 16element.addEventListener('scroll', (e) => { 17 const { scrollTop, scrollHeight, clientHeight } = e.target; 18 const atTop = scrollTop === 0; 19 const atBottom = scrollTop + clientHeight >= scrollHeight; 20 21 if (atTop || atBottom) { 22 // At boundary - overscroll-behavior handles chaining 23 } 24});

Best Practices#

Usage: ✓ Use contain for modals/overlays ✓ Use for isolated scroll areas ✓ Prevent unwanted navigation gestures ✓ Control pull-to-refresh Values: ✓ auto: default browser behavior ✓ contain: prevent chaining only ✓ none: no chaining + no effects Patterns: ✓ Modal dialogs ✓ Dropdown menus ✓ Side drawers ✓ Nested scroll containers Avoid: ✗ Using on document body without reason ✗ Removing all scroll feedback ✗ Breaking expected mobile behaviors ✗ Forgetting touch scrolling

Conclusion#

The overscroll-behavior property controls scroll chaining and boundary effects. Use contain to prevent scroll events from propagating to parent containers, and none to also disable bounce effects. It's essential for modals, dropdowns, and any nested scrolling containers to prevent unexpected scroll behavior.

Share this article

Help spread the word about Bootspring