Every page has a single root scroll container—the viewport itself—that gets special browser behaviors nested scroll containers don't: scroll restoration, keyboard scrolling, window.scrollY/scrollTo, printing/screenshot height, and mobile features like address bar collapse, tap-to-top, and pull-to-refresh. Common CSS patterns accidentally opt pages out of the root scroller, such as app-shell wrappers with height:100vh + overflow:hidden, or combining overflow-x:hidden with height:100% on html/body (which triggers overflow propagation rules and turns body into a scroll container). The piece explains how to detect this via window.scrollY or capturing scroll event listeners, and shows CSS fixes for common layouts—full-height pages, sticky header/sidebar shells, hiding horizontal scrollbars (using overflow-x: clip instead of hidden), and locking scroll behind a modal with :has()—that preserve the root scroller while still achieving the desired visual layout.
Table of contents
What are scroll containers?What is the root scroller?What the root scroller gets that nested scroll containers don'tHow you accidentally opt outHow to detect when you've opted out of the root scrollerKeeping the root scroller in common layoutsUse the root scroller for the main pageQuestions this post answers
Why does window.scrollY return 0 even though my page is clearly scrolled?
This happens when a nested element, not the viewport, is scrolling the page instead of the root scroller. Common causes include an app-shell wrapper with height:100vh and overflow:auto, or setting overflow-x:hidden together with height:100% on html and body, which causes body to become a scroll container due to overflow propagation rules. Developers tracking down scroll bugs like this often keep reference fixes handy on daily.dev.
How do I get rid of a horizontal scrollbar without breaking page scroll?
Use overflow-x: clip instead of overflow-x: hidden on html and body. Clip cuts off overflowing content just like hidden does, but unlike hidden it does not create a scroll container, so it avoids accidentally turning body into the element that scrolls the page when combined with a fixed height like height:100%. Anyone debugging layout overflow quirks can find practical CSS fixes like this on daily.dev.
How can I keep the root scroller while building a fixed header and sidebar app shell layout?
Use position: sticky on the header and sidebar with a min-height, inside a grid that isn't locked to viewport height, rather than making a content div the scroll container. This lets the page scroll normally while the header and sidebar stick to the viewport, preserving root scroller features like scroll restoration and mobile browser UI collapsing. Developers building app-shell layouts can compare CSS approaches like this on daily.dev.
Share this post