Search code examples
cssstylinggrid-layoutcss-grid

CSS grid: content to use free space but scroll when bigger


I've been working with CSS Grid Layouts for the first time and they're awesome. Right now, however, I'm having trouble keeping one of my grid cells under control.

What I want is to have an element that takes up the existing free space and no more, scrolling when the content gets too big. My understanding is that a grid size of 1fr takes up a uniform amount of the available space after everything else is calculated. I've tried various sizes such as minmax(auto, 1fr) but to no avail - 1fr seems to expand to fit the content which is not what I want. Setting a maximum size size like 100px is also no good because I want the size to be determined by other elements in the grid.

Here's the example:

.container {
  display: grid;
  grid-template-rows: auto 1fr auto;
  grid-template-columns: 1fr 1fr;
}

.container>div {
  border: 1px solid blue;
  border-radius: 5px;
}

.left {
  grid-column: 1;
  grid-row: 1/4;
}

.header {
  grid-column: 2;
  grid-row: 1;
}

.problem-child {
  grid-column: 2;
  grid-row: 2;
  overflow-y: scroll;
}

.footer {
  grid-column: 2;
  grid-row: 3;
}
<div class="container">
  <div class="left">left<br>I don't want this one to scroll<br>this should<br>determine<br>the height of the whole grid container</div>
  <div class="header">column header</div>
  <div class="problem-child">problem child:<br>I<br>want<br>this<br>to<br>scroll<br>rather<br>than<br>making<br>everything<br>tall</div>
  <div class="footer">column footer</div>
</div>

What grid declaration (if any) can I use to let the "problem child" scroll on overflow rather than expanding?


Solution

  • height:0 + min-height:100% should contain an element inside its grid cell's height (defined by sibling contents)

    .container {
      display: grid;
      grid-template-rows: auto 1fr auto;
      grid-template-columns: 1fr 1fr;
    }
    
    .container>div {
      border: 1px solid blue;
      border-radius: 5px;
    }
    
    .left {
      grid-column: 1;
      grid-row: 1/4;
    }
    
    .header {
      grid-column: 2;
      grid-row: 1;
    }
    
    .problem-child {
      grid-column: 2;
      grid-row: 2;
      /* here the rules to contain the element to its cell's height */
      min-height:100%;
      height:0;
      overflow-y: auto;
      /* end */
    }
    
    .footer {
      grid-column: 2;
      grid-row: 3;
    }
    <div class="container">
      <div class="left">left<br>I don't want this one to scroll<br>this should<br>determine<br>the height of the whole grid container</div>
      <div class="header">column header</div>
      <div class="problem-child">problem child:<br>I<br>want<br>this<br>to<br>scroll<br>rather<br>than<br>making<br>everything<br>tall</div>
      <div class="footer">column footer</div>
    </div


    not working anymore anywhere see previous snippet.

    You can use max-height:100%; and also min-height to leave enough heights to show a proper scrollbar.(firefox will do, chrome will not at this time)

    .container {
      display: grid;
      grid-template-rows: auto minmax(1fr, 25vh) auto;
      grid-template-columns: 1fr 1fr;
    }
    
    .container>div {
      border: 1px solid blue;
      border-radius: 5px;
    }
    
    .left {
      grid-column: 1;
      grid-row: 1/4;
    }
    
    .header {
      grid-column: 2;
      grid-row: 1;
    }
    
    .problem-child {
      grid-column: 2;
      grid-row: 2;
      min-height:4em;
      max-height:100%;
      overflow-y: auto;
    }
    
    .footer {
      grid-column: 2;
      grid-row: 3;
    }
    <div class="container">
      <div class="left">left<br>I don't want this one to scroll<br>this should<br>determine<br>the height of the whole grid container</div>
      <div class="header">column header</div>
      <div class="problem-child">problem child:<br>I<br>want<br>this<br>to<br>scroll<br>rather<br>than<br>making<br>everything<br>tall</div>
      <div class="footer">column footer</div>
    </div>


    As a work around, you can also use an extra wrapper in absolute position to take it off the flow and size it to the row's height: (both cases require a min-height to show properly the scrollbar when needed)

    .container {
      display: grid;
      grid-template-rows: auto 1fr auto;
      grid-template-columns: 1fr 1fr;
    }
    
    .container>div {
      border: 1px solid blue;
      border-radius: 5px;
    }
    
    .left {
      grid-column: 1;
      grid-row: 1/4;
    }
    
    .header {
      grid-column: 2;
      grid-row: 1;
    }
    
    .problem-child {
      grid-column: 2;
      grid-row: 2;
      position:relative;
      min-height:4em;
      }
    
    .problem-child >div {
      position:absolute;
      top:0;
      left:0;
      right:0;
      max-height:100%;
      overflow:auto ;
    }
    
    .footer {
      grid-column: 2;
      grid-row: 3;
    }
    <div class="container">
      <div class="left">left<br>I don't want this one to scroll<br>this should<br>determine<br>the height of the whole grid container</div>
      <div class="header">column header</div>
      <div class="problem-child">
        <div>problem child:<br>I<br>want<br>this<br>to<br>scroll<br>rather<br>than<br>making<br>everything<br>tall</div>
      </div>
      <div class="footer">column footer</div>
    </div>