demo · v149
Nested Scrollers
When a scroll container is embedded inside another, reaching its end by default propagates scroll momentum to the parent — the classic "scroll chaining" problem. overscroll-behavior: contain on the outer scroller stops the chain at that boundary, while overscroll-attach can reveal UI anchored beyond the edge without propagating to the page.
Each demo has an outer scroller (white, with surrounding content) containing an inner scroller (grey dashed box). Scroll within the grey box. Watch the event log: left column — scroll propagates to outer; right column —
overscroll-behavior-y: contain stops it. Toggle between modes with the buttons.
Outer scroller mode:
Outer scroller
Inner scroller
Overscroll reveal panel
Left: scroll inner past its end
↑ Pulled past top — refresh triggered
Section A — outer content above inner scroller
Section B — scroll down to reach the inner list
▸ Inner list (scroll me)
Inner item 1
Inner item 2
Inner item 3
Inner item 4 — keep scrolling
Inner item 5 — end of inner list
Inner item 6 — really the end
Section C — outer content below inner
Section D — more outer content
Section E — even more
Section F — last outer item
Scroll the inner list to see scroll chaining behavior…
Right: overscroll-attach reveal
↑ Refresh action revealed
Outer content — this scroller has overscroll-behavior-y: contain
The inner scroller below also has contain
▸ Inner list (scroll to end)
Feed item 1
Feed item 2
Feed item 3
Feed item 4
Feed item 5 — end of inner feed
More outer content
Inner overscroll is contained
Outer overscroll reveals the green panel above
Try scrolling the outer container past the top
Scroll the outer container past the top to see the reveal panel…
/* Inner scroller: always contain, never propagate to parent */
.inner-list {
overflow-y: scroll;
overscroll-behavior-y: contain;
}
/* Outer scroller: contain — stops inner overscroll from reaching page */
.outer-scroll {
overflow-y: scroll;
overscroll-behavior-y: contain;
}
/* Without contain on the outer: scrolling the inner past its end
propagates momentum to the outer, then to the page — unintentional
page navigation during a list-within-a-panel interaction. */
/* With contain on both:
inner overscroll → stopped at inner boundary
outer overscroll → stopped at outer boundary
page scroll → unaffected by either list */
/* Reveal pattern: sticky element above top of outer scroller */
.pull-panel {
position: sticky;
top: 0;
transform: translateY(-100%); /* hidden above viewport */
transition: transform 0.3s;
}
/* JavaScript detects overscroll and toggles .visible: */
.pull-panel.visible { transform: translateY(0); }
see also
implementation reference
Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗