<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1" -->

---
title: The root scroller and how not to lose it | daily.dev
description: Every page has a single root scroll container—the viewport itself—that gets special browser behaviors nested scroll containers don&#x27;t: scroll restoration,...
canonical: https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: The root scroller and how not to lose it | daily.dev
og:description: Every page has a single root scroll container—the viewport itself—that gets special browser behaviors nested scroll containers don&#x27;t: scroll restoration,...
og:url: https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1
og:image: https://api.daily.dev/og/posts/uJk7Eemn1.png
og:image:alt: The root scroller and how not to lose it
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# The root scroller and how not to lose it

**[Polypane](https://daily.dev/sources/polypane)** · 16 min read · 0 upvotes · 0 comments

## Summary

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.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://polypane.app/blog/the-root-scroller-and-how-not-to-lose-it>

## Questions 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._

## Similar posts on daily.dev

- [Fixing full-bleed CSS](https://daily.dev/posts/fixing-full-bleed-css-jyiwcav6l) · dbushell · 29 upvotes · 3 comments
- [Why Keyboard Users Can’t Scroll Your Overflow Containers](https://daily.dev/posts/why-keyboard-users-can-t-scroll-your-overflow-containers-39v9wmg2j) · CSS-Tricks · 5 upvotes · 0 comments
- [Using 100vw is now scrollbar-aware \(in Chrome 145\+, under the right conditions\)](https://daily.dev/posts/using-100vw-is-now-scrollbar-aware-in-chrome-145-under-the-right-conditions--52isi4cnv) · Bram.us · 0 upvotes · 0 comments

---

Tags: [#css](https://daily.dev/tags/css), [#polypane](https://daily.dev/tags/polypane)

[View this post on daily.dev](https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"The root scroller and how not to lose it","url":"https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1"},"datePublished":"2026-09-23T10:26:44.260Z","dateModified":"2026-09-23T10:27:08.573Z","description":"Every page has a single root scroll container—the viewport itself—that gets special browser behaviors nested scroll containers don't: scroll restoration,...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7d0530938de044cfc74c9204a7507506?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7d0530938de044cfc74c9204a7507506?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Polypane","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Polypane","logo":"https://media.daily.dev/image/upload/s--KIZm_n0x--/f_auto,q_auto/v1782646521/logos/polypane?_a=BAMAMicg0","url":"https://daily.dev/sources/polypane"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"css,polypane","timeRequired":"PT16M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Polypane","item":"https://daily.dev/sources/polypane"},{"@type":"ListItem","position":3,"name":"The root scroller and how not to lose it"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/the-root-scroller-and-how-not-to-lose-it-ujk7eemn1#faq","mainEntity":[{"@type":"Question","name":"Why does window.scrollY return 0 even though my page is clearly scrolled?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"How do I get rid of a horizontal scrollbar without breaking page scroll?","acceptedAnswer":{"@type":"Answer","text":"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."}},{"@type":"Question","name":"How can I keep the root scroller while building a fixed header and sidebar app shell layout?","acceptedAnswer":{"@type":"Answer","text":"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."}}]}
```

