Allow one grid item to scroll with fixed header and sidebar

14,126

Solution 1

For overflow: auto to work (i.e., for scrollbars to render) browsers need a trigger. This trigger is usually a height / width limitation that forces an overflow condition, which launches the scrollbars.

Trigger conditions vary among browsers. They also vary among CSS technologies, such as flex, grid and block layouts.

In this particular case, there are several logical places to establish an overflow condition, but none of them work.

  1. You could target the grid item, as you have tried:

    .content {
       height: 1000px
       overflow: auto;
    }
    

    But it doesn't work. No scrollbar appears on the fluid item.

body {
  margin: 0;
  /* overflow: hidden; */
}

.page {
  display: grid;
  grid-template-rows: 55px auto;
  grid-template-columns: 20vh auto;
  grid-template-areas: "nav header" 
                       "nav content";
}

.nav {
  grid-area: nav;
  background-color: aqua;
}

.header {
  grid-area: header;
  background-color: lightgrey;
}

.content {
  grid-area: content;
  height: 1000px;
  overflow: auto;
  background-color: red;
}
<div class="page">
  <div class="nav">Side nav</div>
  <div class="header">Header</div>
  <div class="content">
    <h1>title</h1>
  </div>
  <div>
  1. You could target the row itself, as I tested:

    .page {
       display: grid;
       grid-template-rows: 55px 1000px;
    }
    
    .content {
       overflow: auto;
    }
    

    But that doesn't work either. Still no scrollbar on the fluid item.

body {
  margin: 0;
  /* overflow: hidden; */
}

.page {
  display: grid;
  grid-template-rows: 55px 1000px;
  grid-template-columns: 20vh auto;
  grid-template-areas:
    "nav header"
    "nav content";
}

.nav {
  grid-area: nav;
  background-color: aqua;
}

.header {
  grid-area: header;
  background-color: lightgrey;
}

.content {
  overflow: auto;
  grid-area: content;
  background-color: red;
}
<div class="page">
  <div class="nav">Side nav</div>
  <div class="header">Header</div>
  <div class="content">
    <h1>title</h1>
  </div>
  <div>
  1. So I targeted a child of the grid item. DING DING DING! That worked.

    No need for fixed positioning. No need for sticky positioning. This works across all browsers that support Grid Layout.

body {
  margin: 0;
}

.page {
  display: grid;
  grid-template-rows: 55px calc(100vh - 55px);  /* height limitation on second row */
  grid-template-columns: 20vh auto;
  grid-template-areas: "nav header" 
                       "nav content";
}

.nav {
  grid-area: nav;
  background-color: aqua;
}

.header {
  grid-area: header;
  background-color: lightgrey;
}

.content {
  grid-area: content;
  background-color: red;
  overflow: auto;                          /* overflow condition on parent */
}

article {
  height: 1000px;                          /* height set on child; triggers scroll */
}
<div class="page">
  <div class="nav">Side nav</div>
  <div class="header">Header</div>
  <div class="content">
    <article><!-- new section for content -->
      <h1>title</h1>
    </article>
  </div>
  <div>

jsFiddle demo

Solution 2

Browser support is not 100%, but what about actually using sticky instead of fixed positioning? (now tested in Chrome) You won't have to deal with hard-coded margins.

One of the issues you'll still have to deal with, what to do when the content in your sidebar (.nav > div) Is higher than your viewport.

body {
  margin: 0;
}

.page {
  display: grid;
  grid-template-rows: 55px auto;
  grid-template-columns: 3.5rem auto;
  grid-template-areas: "nav header" "nav content";
}

.nav {
  grid-area: nav;
  background-color: blue;
}

.nav > div {
  position: sticky;
  top: 0;
}

.header {
  grid-area: header;
  background-color: grey;
  position: sticky;
  top: 0;
  min-height: 3.5rem;
}

.content {
  grid-area: content;
  min-height: 1000px;
  background-color: red;
}
<div class="page">
  <div class="nav">
    <div>Side nav</div>
  </div>
  <div class="header">Header</div>
  <div class="content">
    <h1>title</h1>
  </div>
<div>

Solution 3

I have included the change log to see where the code needs to be change in order to get an understanding. Also the full code snippet is available below. Hope this is what you expect.

Change log

*Remove body { overflow: hidden; }

*Change .page { grid-template-columns: 3.5rem auto; }

*Added

.nav { position: fixed;
      top: 0;
      bottom:0;}

*Added

.header {  position: fixed;
      margin-left: 3.5rem;
      width: 100%;
      height: 3.5rem; }

Full Code

body {
  margin: 0;
}

.page {
  display: grid;
  grid-template-rows: 55px auto;
  grid-template-columns: 3.5rem auto;
  grid-template-areas:
    "nav header"
    "nav content";
}

.nav {
  position: fixed;
  top: 0;
  bottom:0;
  grid-area: nav;
  background-color: blue;

}

.header {
  grid-area: header;
  background-color: grey;
  position: fixed;
  margin-left: 3.5rem;
  width: 100%;
  height: 3.5rem;
}

.content {
  grid-area: content;
  height: 1000px;
  background-color: red;
}
<div class="page">
  <div class="nav">Side nav</div>
  <div class="header">Header</div>
  <div class="content">
    <h1>title</h1>
  </div>
<div>
Share:
14,126
sss
Author by

sss

Updated on June 11, 2022

Comments

  • sss
    sss about 2 years

    I have a grid layout with two columns and two rows. A sticky left nav, a sticky header, and content that will live in the bottom right corner of the grid.

    What I have now is nearly there, but I would like the .content div to use scroll when content extends beyond the screen. I thought I would be able to just use overflow: auto, but that isn't working. Is what I have close?

    body {
      margin: 0;
      overflow: hidden;
    }
    
    .page {
      display: grid;
      grid-template-rows: 55px auto;
      grid-template-columns: 20vh auto;
      grid-template-areas: "nav header" "nav content";
    }
    
    .nav {
      grid-area: nav;
      background-color: blue;
    }
    
    .header {
      grid-area: header;
      background-color: grey;
    }
    
    .content {
      grid-area: content;
      height: 1000px; // This is dynamic
      background-color: red;
    }
    <div class="page">
      <div class="nav">Side nav</div>
      <div class="header">Header</div>
      <div class="content">
        <h1>title</h1>
      </div>
      <div>

    JS fiddle