body { overflow-x: hidden; } breaks position: sticky

Viewed 29112

I have an element that I am making sticky with position sticky:

#header {
    position: sticky;
    width: 100vw;
    top: 0;
}
<app-header id="header"></app-header>

And that works fine, but I realised that if I use:

body {
  overflow-x: hidden;
}

That breaks sticky, and I need to set body overflow-x to hidden, how can I fix that, with only CSS solution, no JS solutions?

10 Answers

UPDATE:

This has been successfully tested on Safari v12.0.2, Firefox v64.0, and Chrome v71.0.3578.98

Added position: -webkit-sticky; for Safari.


Unfortunately the spec is not too clear about the implications of overflow-x: hidden; on position sticky, but there is a way to fix this. Thankfully there is an issue to hopefully fix this: https://github.com/w3c/csswg-drafts/issues/865.

The simple solution is to remove or unset overflow-x: hidden; from every ancestor of the element you want to have position: sticky;. Then you can have overflow-x: hidden; on the body and it will work!

Also double check that you don't have overflow set on both the body and html tags which I posted about more in depth here: https://stackoverflow.com/a/54116725/6502003

Here is a pen if you want to play around with it: https://codepen.io/RyanGarant/pen/REYPaJ

/* 
  Try commenting out overflow on body style and uncommenting
  overflow on .overflow-x-hidden class and you will see 
  position sticky stop working!  
*/

body {
  overflow-x: hidden;
}

.overflow-x-hidden {
/*   overflow-x: hidden; */
  border: 1px solid blue;
}

h1 {
  background: palevioletred;
  color: #fff;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
}

.tall {
  background: linear-gradient(to bottom, paleturquoise, white);
  height: 300vh;
  width: 100%;
}
<div class="overflow-x-hidden">
  <h1>I want to be sticky!</h1>
  
  <div class="tall"></div>
</div>

Setting the overflow-x property to value clip helped me achieve position sticky and prevent scrolling.

Here is more explanation in this article

Not sure if this helps, but I struggled with this for mobile (where the page would "overflow" and scroll past the boundaries) for so long and finally fixed it.

I tried the body {overflow: hidden;} solution, but it would disable my sticky elements. So I then created a <div class="container"> that contained all of my other divs of the page inside the <body>, and that seemed to do it!

I just want to add this here if anyone runs into a similar issue. position:sticky was not working for me because we are applying overflow-y:scroll on tablet/mobile screen resolutions. overflow: and position:sticky do not work together. This article explains the 2 workarounds to fix it. I went with setting a fixed height on the table and then hiding the vertical scroll bar with

::-webkit-scrollbar {
  width: 0;  
  background: transparent;  
}::-webkit-scrollbar {
  width: 0;  
  background: transparent;  /* 
}

This is how I make it work for me:
Create a parent wrapper/container element that should contains all your other page elements, and give it a fixed height (mostly height: 100vh) with overflow-y: auto and overflow-x: hidden as it's described in the code/snippet below ..
and make the body overflow: hidden to avoid any extra scroll bars ..

This solution may comes with some consequences/restrictions, for example:
if you're used to use the CSS scroll-behavior property in the html element you will need then to move it to the #page_wrapper element instead ..

body {
  margin: 0;
  overflow: hidden;
}

.page-wrapper {
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
}

.sticky {
  position: sticky;
  left: 0;
  padding: 4px;
  text-align: center;
  background-color: forestgreen;
}

.sticky-header {
  top: 0;
}
.sticky-footer {
  bottom: 0;
}

.long-content {
  padding: 2rem;
  min-height: 100vh;
}

.break-overflow-x {
  min-width: 200vw;
  padding: 2rem;
  background-color: red;
}
<body>
  <div class="page-wrapper">
    <div class="sticky sticky-header"><button>Sticky Header</button></div>
    <div class="long-content">Long vertical content</div>
    <div class="break-overflow-x">Long horizontal content</div>
    <div class="long-content">Long vertical content</div>
    <div class="sticky sticky-footer"><button>Sticky Footer</button></div>
  </div>
</body>

body, html { overflow-x: clip; } 

This works well with position: sticky

Im using transform: translateX(-100px) for load animations and overflow-x:clip prevents expanding the viewport and showing horizontal scrollbar..

I think you have to fix :

body {
  overflow-x: clip;
}

I hope this works .

can you try with the following...

#one { position: sticky; top: 10px; }* {
  box-sizing: border-box;
}
body{overflow-x: hidden;}
dl > div {
  background: #FFF;
  padding: 24px 0 0 0;
}

dt {
  background: #B8C1C8;
  border-bottom: 1px solid #989EA4;
  border-top: 1px solid #717D85;
  color: #FFF;
  font: bold 18px/21px Helvetica, Arial, sans-serif;
  margin: 0;
  padding: 2px 0 0 12px;
  position: -webkit-sticky;
  position: sticky;
  top: -1px;
}

dd {
  font: bold 20px/45px Helvetica, Arial, sans-serif;
  margin: 0;
  padding: 0 0 0 12px;
  white-space: nowrap;
}

dd + dd {
  border-top: 1px solid #CCC;
}
<!-- Learn about this code on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/position -->

<dl>
  <div>
    <dt>A</dt>
    <dd>Andrew W.K.</dd>
    <dd>Apparat</dd>
    <dd>Arcade Fire</dd>
    <dd>At The Drive-In</dd>
    <dd>Aziz Ansari</dd>
  </div>
  <div>
    <dt>C</dt>
    <dd>Chromeo</dd>
    <dd>Common</dd>
    <dd>Converge</dd>
    <dd>Crystal Castles</dd>
    <dd>Cursive</dd>
  </div>
  <div>
    <dt>E</dt>
    <dd>Explosions In The Sky</dd>
  </div>
  <div>
    <dt>T</dt>
    <dd>Ted Leo &amp; The Pharmacists</dd>
    <dd>T-Pain</dd>
    <dd>Thrice</dd>
    <dd>TV On The Radio</dd>
    <dd>Two Gallants</dd>
  </div>
</dl>
  

For those who have the same issue. I added overflow: hidden to the div below the header holding all the contents.

 <div class="header" style="position: sticky;">
        //navs
 </div>
 <div class="content-div" style="overflow: hidden;">
       //contents 
 </div>
Related