Two Rules, No JavaScript The four lines of CSS that killed my scroll-lock JavaScript


You open a modal. You scroll inside it. The page behind the modal scrolls instead, the modal sits there like it didn’t notice, and by the time you close the thing you’re four hundred pixels from where you started.
I have shipped a fix for this. So has every frontend developer I know. It’s always document.body.style.overflow = ‘hidden’ on open, the inverse on close, and then a second patch two weeks later because killing the scrollbar shoved the whole layout eight pixels sideways and someone noticed. Then a third patch for iOS, which ignores all of it anyway. Twelve lines of state management to stop a page from doing the one thing pages do.
The correct fix has existed in CSS since 2017. It just didn’t work.
What overscroll-behavior actually does
overscroll-behavior controls what happens when you hit the edge of a scrolling area. Set it to contain and scroll chaining stops – the scroll dies at the boundary instead of handing itself off to whatever is underneath. Pull-to-refresh, rubber-banding, the page creeping along behind an overlay: all of it, one property.
The catch was brutal in its simplicity. overscroll-behavior only did anything if the element was actually scrollable. One pixel of overflow, minimum. A dialog that fits its content has nothing to overflow, so the property sat there being decorative, and everyone went back to writing JavaScript.
Chrome 144 changed that on 13 January 2026. overscroll-behavior now applies to non-scrollable scroll containers too, which is what the specification said all along. Edge picked it up eight days later.
The snippet
This is the shape that went around in November, from Bramus Van Damme’s original post:
dialog {
overscroll-behavior: contain;
}
dialog::backdrop {
overflow: hidden;
overscroll-behavior: contain;
}
The load-bearing line is overflow: hidden on the ::backdrop. That’s what turns the backdrop into a scroll container in the first place – a non-scrollable one, which until January was the same as not having one. Since the backdrop covers the entire viewport, any scroll gesture that lands on it now hits a wall instead of chaining down to the document.
No JavaScript. No layout shift. No cleanup on close, because there’s nothing to clean up.
Firefox shipped it
This is the part that changed since the original post went up, and it’s the reason I wanted to write this one.
Bramus closed with a hope that other vendors would follow. Gecko bug 1837436 is now RESOLVED FIXED, and the fix rode out in Firefox 150 on 21 April 2026 – desktop and Android together.
The bug is worth reading if you like watching bánh mì get made. Filed in 2023 by Šime Vidas, sat at P3 for two years, then Chromium fixed it and it turned into an interop problem overnight. Hiroyuki Ikezoe took it, wrote the patch, and it got backed out for failing a web platform test on Android. The reason: the test Google had landed was missing a meta viewport tag, so the document scaled down until the root scroll container had no room to scroll and the assertion failed on a real mobile environment. His comment on the fix is the driest thing in the whole thread – a note that Google should have run the tests on a valid mobile environment. Then he added the viewport tag, relanded, and it stuck.
Somebody linked Bramus’ blog post directly in the bug thread, incidentally, seven months ago. Write-ups move things.
Safari, though
WebKit bug 243452 was filed in August 2022. Status: NEW. Assignee: Nobody. Four years, eleven people on the CC list, and the only substantive engineering comment is Simon Fraser in June 2024 saying that implementing it would mean representing non-scrollable boxes with overscroll-behavior: contain in the scrolling tree, which is a fair amount of work.
I believe him. Scrolling trees are load-bearing and terrifying and nobody wants to be the person who broke momentum scrolling on an iPhone. It still makes me want to put my head on the desk, because I have written the JavaScript workaround more times than I have written a cover letter, and I would love to stop.
The last two comments on the bug are just people showing up to report that everyone else has shipped. Chrome in December, Firefox in May. That’s the entire conversation now: a scoreboard with one empty column.
The refinement: scope it to :modal
Under the original post, a commenter named Anup made a point that improves the snippet, and Bramus agreed. Paraphrasing: you probably only want this on a dialog opened as a modal. A modal is supposed to be the only thing you can interact with. A non-modal dialog opened with show() is a different animal – it sits alongside the page, and being able to scroll the background to check something might be exactly what the user wants.
The :modal pseudo-class does that scoping for free:
dialog:modal {
overscroll-behavior: contain;
}
dialog:modal::backdrop {
overflow: hidden;
overscroll-behavior: contain;
}
Half of this is belt and braces – a non-modal dialog never enters the top layer, so it never gets a ::backdrop to style. That rule was already modal-only by accident. The first rule wasn’t, and now it is.
The other reason to be explicit: popovers also render a ::backdrop, they’re also in the top layer, and they are emphatically not modal. Light dismiss is the whole point of a popover. A bare ::backdrop selector in your stylesheet would quietly scroll-lock those too, which is the opposite of what a popover is for. And since dialog accepts a popover attribute, even the dialog-scoped version isn’t automatically safe.
:modal has been Baseline since September 2022 – Chrome and Edge 105, Firefox 103, Safari 15.6. Free to use, no fallback needed.

Where this leaves you today
Three separate features are in play here, and they don’t share a support table:
- The <dialog> element
95.9% global support. Chrome 37, Firefox 98, Safari 15.4. Settled. - ::backdrop
96.1%. Chrome 37, Firefox 47, Safari 15.4. Also settled. - overscroll-behavior the property
94.13%. Everywhere except Opera Mini and IE. - overscroll-behavior on containers with no overflow
The thing this whole post is about. Chrome and Edge 144+, Firefox 150+, Safari nowhere.
Watch that last row, because caniuse won’t tell you about it. The overscroll-behavior table has shown a green tick next to Safari 16 since 2022 and it is not lying – Safari supports the property. It just doesn’t support the one case that makes this snippet work. Feature tables track properties; bugs live one level down. Release notes and bug trackers are where the real answer is.
Practically: ship the CSS now. It costs four lines, it’s inert in Safari, and roughly seven in ten of your users get the good behavior today. If your modals genuinely break when the background scrolls, keep your JavaScript fallback behind a feature check and delete it the day WebKit lands. If they just look a bit silly, drop the JavaScript and let Safari be a little silly.
That’s the whole post. Four lines of CSS, one pseudo-class, and a scrollbar-shift patch you never have to write again.
Credit where it’s due
This is an updated take on Use overscroll-behavior: contain to prevent a page from scrolling while a <dialog> is open by Bramus Van Damme, published 25 November 2025. His post is where I learned this, and the original snippet is his. The :modal suggestion comes from Anup in the comments there.
The original is licensed under CC BY 4.0, code samples under the MIT License. Same terms apply here. Go read the original.