Search code examples
htmlcssoverflowcss-grid

overflow scroll breaks in nested grid


I have two CSS-Grids. One within the other. I don't want the whole page to scroll, but only the contents of a grid-area of the nested grid. This nested grid should fill all available space. However overflow:scroll does not work within this nested grid.

As you can see in the simplified example below, the div with the class .aside works perfectly while the div with .bottomleft does not scroll at all.

The div-heights start to break with the .main-container - div, but I have no idea why.

What really confuses me is why everything works fine in the .aside - div. The only difference that I can see here is that it is not in the nested grid.

Naturally, everything works perfectly if the .bottom-left - div or the second row of the .main-container - grid are given a fixed height, but the goal is to make it variable.

I also tried adding various max-heights and heights to other parent divs, but was not successful so far.

Thank you!

https://jsfiddle.net/vs6c4gq9/3/

html,
body {
  height: 100%;
  overflow: hidden;
}

#root {
  height: 100%;
}

.container {
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-template-rows: auto auto;
  grid-template-areas: 'nav nav ' 'aside main';
  height: 100%;
}

.header {
  grid-area: nav;
  background-color: lightblue;
}

.main {
  grid-area: main;
  background-color: lightpink;
  height: 100%;
}

.aside {
  grid-area: aside;
  overflow-y: scroll;
}

.main-container {
  display: grid;
  grid-template-columns: 1fr 3fr;
  grid-template-rows: auto auto;
  grid-template-areas: 'topleft topright' 'bottomleft bottomright';
  height: 100%;
  
}

.topleft {
  grid-area: topleft;
}

.topright {
  grid-area: topright;
}

.bottomleft {
  grid-area: bottomleft;
  overflow-y: scroll;
  height: 100%;
}

.bottomright {
  grid-area: bottomright;
}
<div id="root">
  <div class="container">
    <div class="header">
      header
    </div>
    <div class="main">
      <div class="main-container">
        <div class="topleft">
          topleft
        </div>
        <div class="topright">
          topright
        </div>
        <div class="bottomleft">
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>
          <div>bottomleft</div>

          <div>last</div>

        </div>
        <div class="bottomright">
          bottomright
        </div>
      </div>
    </div>
    <div class="aside">
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>aside</div>
      <div>last</div>
    </div>
  </div>
</div>


Solution

  • You're right in the sense that there doesn't seem to be any reason why the scroll function works on one element (.aside), but not on another (.bottomleft). There doesn't appear to be any material difference. One element is a nested grid container. But that shouldn't matter.

    However, if you look at the bigger picture, neither scrollbar should work.

    The overflow property normally requires a fixed length to generate a scroll bar. Without such a limit, the element simply expands to accommodate content, and there's no possibility for an overflow.

    Such is the case in your code: Both overflow elements are set to height: auto.

    .container {
      display: grid;
      grid-template-columns: 1fr 3fr;
      grid-template-rows: auto auto; <-- second row (where 'aside' is placed) is
                                         set to content-based height
      grid-template-areas: 'nav nav ' 'aside main';
      height: 100%;
    }
    
    .main-container {
      display: grid;
      grid-template-columns: 1fr 3fr;
      grid-template-rows: auto auto; <-- second row (where 'bottomright' is placed) is
                                         also set to content-based height
      grid-template-areas: 'topleft topright' 'bottomleft bottomright';
      height: 100%;
    }
    

    Now refer to this rule, as described in MDN:

    In order for overflow to have an effect, the block-level container must have either a set height (height or max-height) or white-space set to nowrap.

    Hence, the scroll function in your code should fail in both cases. The fact that one works in at least one browser suggests an anomaly or an intervention. In either case, I would suggest it's unreliable.

    Consider a trade-off: Sacrifice some flexibility in return for more stability and security. Here is a modified version of your code:

    revised fiddle

    .container {
      display: grid;
      grid-template-columns: 1fr 3fr;
      grid-template-rows: 25px calc(100vh - 25px); /* adjustment */
      grid-template-areas: 'nav nav ' 'aside main';
      height: 100vh;
    }
    
    .header {
      grid-area: nav;
      background-color: lightblue;
    }
    
    .main {
      grid-area: main;
      background-color: lightpink;
    }
    
    .aside {
      grid-area: aside;
      overflow-y: scroll;
    }
    
    .main-container {
      display: grid;
      grid-template-columns: 1fr 3fr;
      grid-template-rows: 25px calc(100vh - 50px);  /* adjustment */
      grid-template-areas: 'topleft topright' 'bottomleft bottomright';
    }
    
    .topleft {
      grid-area: topleft;
    }
    
    .topright {
      grid-area: topright;
    }
    
    .bottomleft {
      grid-area: bottomleft;
      overflow-y: scroll;
    }
    
    .bottomright {
      grid-area: bottomright;
    }
    
    body {
      margin: 0; /* remove default margins */
    }
    <div id="root">
      <div class="container">
        <div class="header">
          header
        </div>
        <div class="main">
          <div class="main-container">
            <div class="topleft">
              topleft
            </div>
            <div class="topright">
              topright
            </div>
            <div class="bottomleft">
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
              <div>bottomleft</div>
    
              <div>last</div>
    
            </div>
            <div class="bottomright">
              bottomright
            </div>
          </div>
        </div>
        <div class="aside">
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>aside</div>
          <div>last</div>
        </div>
      </div>
    </div>