Search code examples
htmlcsssticky

left-side sticky element disappears when scrolling to the right


I am trying to create a web page with five regions, three of them are sticky. The sticky feature works fine when the user scrolls down, but the elements which should stick to the left just disappear when the window is scrolled to the right past the width of one viewport. I don't know how wide the data will be in advance, which is why I am trying to allow the elements to automatically expand to fit the contents. Is there a way to fix this so the left elements stay visible as the user scrolls all the way to the right?

The regions are described as follows:

  1. header - This region should disappear when the user scrolls vertically.

  2. upperleft - This region is a small column header which sticks to the left and top while scrolling.

  3. upperright - This region should stick only to the top when scrolling vertically. It should disappear behind upperleft when the user scrolls to the right.

  4. bottomleft - This region should stick to the left of the screen when the user scrolls right and disappear behind upperleft when the user scrolls down.

  5. bottomright - This region should disappear behind upperleft, upperright, and bottomleft depending on how the user scrolls.

Here is an example which demonstrates the problem (I am using Firefox 62.0.3):

body {
  margin: 1rem;
  display: grid;
  grid-template-columns: 3rem auto;
  grid-template-rows: 12vh 2rem auto;
  grid-template-areas: "header header" "topleft topright" "bottomleft bottomright";}

.header {
  position: fixed;
  grid-area: header;
  display: block;
  background: white;
  text-align: center;
  width: 100vw;
}

.topleft {
  grid-area: topleft;
  background-color: #bababa;
  border: solid 1px black;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  left: 0;
  z-index: 2;
}

.topright {
  grid-area: topright;
  background-color: #c0c0c0;
  border: solid 1px black;
  box-sizing: border-box;
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 1;
  white-space: nowrap;
  display: inline-block;
  width: 300vw; /* simulate large horizontal data set */
}

.bottomleft {
  grid-area: bottomleft;
  background-color: #c0c0c0;
  border: solid 1px black;
  box-sizing: border-box;
  position: -webkit-sticky;
  position: sticky;
  left: 0;
  z-index: 1;
  height: 300vh; /* simulate large vertical data set */
}

.bottomright {
  grid-area: bottomright;
  background-color: #cacaca;
  border: solid 1px brown;
  box-sizing: border-box;
  text-align: left;
  display: inline-block;
  height: 300vh; /* simulate large vertical data set */
  width: 300vw; /* simulate large horizontal data set */
}
<div class="header">
This intro area should be visible until scrolled out of view.
</div>
<div class="topleft">
Stay
</div>
<div class="topright">
Top line sticky vertically but not horizontally
</div>
<div class="bottomleft">
<p>1</p>
<p>2</p>
<p>3</p>
<p>4</p>
<p>5</p>
<p>6</p>
<p>7</p>
<p>8</p>
<p>9</p>
</div>
<div class="bottomright">
Large area with cells
</div>


Solution

  • You are having an overlfow issue, your body is a block element so its width won't exceed the dimension of your screen which create the issue of left sidebar.

    An easy fix is to make the body inline-grid:

    body {
      margin: 1rem;
      display: inline-grid;
      grid-template-columns: 3rem auto;
      grid-template-rows: 12vh 2rem auto;
      grid-template-areas: "header header" "topleft topright" "bottomleft bottomright";}
    
    .header {
      position: fixed;
      grid-area: header;
      display: block;
      background: white;
      text-align: center;
      width: 100vw;
    }
    
    .topleft {
      grid-area: topleft;
      background-color: #bababa;
      border: solid 1px black;
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      left: 0;
      z-index: 2;
    }
    
    .topright {
      grid-area: topright;
      background-color: #c0c0c0;
      border: solid 1px black;
      box-sizing: border-box;
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      z-index: 1;
      white-space: nowrap;
      display: inline-block;
      width: 300vw; /* simulate large horizontal data set */
    }
    
    .bottomleft {
      grid-area: bottomleft;
      background-color: #c0c0c0;
      border: solid 1px black;
      box-sizing: border-box;
      position: -webkit-sticky;
      position: sticky;
      left: 0;
      z-index: 1;
      height: 300vh; /* simulate large vertical data set */
    }
    
    .bottomright {
      grid-area: bottomright;
      background-color: #cacaca;
      border: solid 1px brown;
      box-sizing: border-box;
      text-align: left;
      display: inline-block;
      height: 300vh; /* simulate large vertical data set */
      width: 300vw; /* simulate large horizontal data set */
    }
    <div class="header">
    This intro area should be visible until scrolled out of view.
    </div>
    <div class="topleft">
    Stay
    </div>
    <div class="topright">
    Top line sticky vertically but not horizontally
    </div>
    <div class="bottomleft">
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <p>4</p>
    <p>5</p>
    <p>6</p>
    <p>7</p>
    <p>8</p>
    <p>9</p>
    </div>
    <div class="bottomright">
    Large area with cells
    </div>

    To better illustrate the issue you can add border to the initial code and you will see that the left sidebar will reach this border and then stop:

    body {
      margin: 1rem;
      display: grid;
      border:2px solid red;
      grid-template-columns: 3rem auto;
      grid-template-rows: 12vh 2rem auto;
      grid-template-areas: "header header" "topleft topright" "bottomleft bottomright";}
    
    .header {
      position: fixed;
      grid-area: header;
      display: block;
      background: white;
      text-align: center;
      width: 100vw;
    }
    
    .topleft {
      grid-area: topleft;
      background-color: #bababa;
      border: solid 1px black;
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      left: 0;
      z-index: 2;
    }
    
    .topright {
      grid-area: topright;
      background-color: #c0c0c0;
      border: solid 1px black;
      box-sizing: border-box;
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      z-index: 1;
      white-space: nowrap;
      display: inline-block;
      width: 300vw; /* simulate large horizontal data set */
    }
    
    .bottomleft {
      grid-area: bottomleft;
      background-color: #c0c0c0;
      border: solid 1px black;
      box-sizing: border-box;
      position: -webkit-sticky;
      position: sticky;
      left: 0;
      z-index: 1;
      height: 300vh; /* simulate large vertical data set */
    }
    
    .bottomright {
      grid-area: bottomright;
      background-color: #cacaca;
      border: solid 1px brown;
      box-sizing: border-box;
      text-align: left;
      display: inline-block;
      height: 300vh; /* simulate large vertical data set */
      width: 300vw; /* simulate large horizontal data set */
    }
    <div class="header">
    This intro area should be visible until scrolled out of view.
    </div>
    <div class="topleft">
    Stay
    </div>
    <div class="topright">
    Top line sticky vertically but not horizontally
    </div>
    <div class="bottomleft">
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <p>4</p>
    <p>5</p>
    <p>6</p>
    <p>7</p>
    <p>8</p>
    <p>9</p>
    </div>
    <div class="bottomright">
    Large area with cells
    </div>

    As a side note, it's good to avoid using the body as a container, better rely on your own container to make it easy to add more structure later or to integrate your code elsewhere

    body {
     margin:0;
    }
    .container {
      margin: 1rem;
      display: inline-grid;
      border:2px solid red;
      grid-template-columns: 3rem auto;
      grid-template-rows: 12vh 2rem auto;
      grid-template-areas: "header header" "topleft topright" "bottomleft bottomright";}
    
    .header {
      position: fixed;
      grid-area: header;
      display: block;
      background: white;
      text-align: center;
      width: 100vw;
    }
    
    .topleft {
      grid-area: topleft;
      background-color: #bababa;
      border: solid 1px black;
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      left: 0;
      z-index: 2;
    }
    
    .topright {
      grid-area: topright;
      background-color: #c0c0c0;
      border: solid 1px black;
      box-sizing: border-box;
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      z-index: 1;
      white-space: nowrap;
      display: inline-block;
      width: 300vw; /* simulate large horizontal data set */
    }
    
    .bottomleft {
      grid-area: bottomleft;
      background-color: #c0c0c0;
      border: solid 1px black;
      box-sizing: border-box;
      position: -webkit-sticky;
      position: sticky;
      left: 0;
      z-index: 1;
      height: 300vh; /* simulate large vertical data set */
    }
    
    .bottomright {
      grid-area: bottomright;
      background-color: #cacaca;
      border: solid 1px brown;
      box-sizing: border-box;
      text-align: left;
      display: inline-block;
      height: 300vh; /* simulate large vertical data set */
      width: 300vw; /* simulate large horizontal data set */
    }
    <div class="container">
    <div class="header">
    This intro area should be visible until scrolled out of view.
    </div>
    <div class="topleft">
    Stay
    </div>
    <div class="topright">
    Top line sticky vertically but not horizontally
    </div>
    <div class="bottomleft">
    <p>1</p>
    <p>2</p>
    <p>3</p>
    <p>4</p>
    <p>5</p>
    <p>6</p>
    <p>7</p>
    <p>8</p>
    <p>9</p>
    </div>
    <div class="bottomright">
    Large area with cells
    </div>
    </div>