Search code examples
htmlcssoverflowcss-grid

Css grid auto sized row content with overflow


I've been having problems with implementing a nested grid structure. This is a simplified example. As you can see the nested structure gives the height for the outer structure, thus growing outside the body. What I'd like to achieve is to make the .content__inner element, and only that element scrollable. Everything else should be inside the viewport. How can I achieve that?

Edit 1: Just to clarify, I'm using 100vh on the body, but don't want to set it on the container. The container should have the height of its parent, whatever it is.

body {
  display: block;
  height: 100vh;
  width: 100%;
  background-color: white;
  margin: 0;
}

.container {
  max-height: 100%;
  display: grid;
  grid-template-areas: "header header" "side content" "footer footer";
  grid-template-columns: 50px auto;
  grid-template-rows: min-content minmax(min-content, max-content) min-content;
}

.header {
  grid-area: header;
  background-color: rgba(255, 255, 0, 0.3);
}

.side {
  grid-area: side;
  background-color: rgba(0, 255, 0, 0.3);
}

.content {
  grid-area: content;
  background-color: rgba(0, 0, 255, 0.3);
}

.overflow {
  overflow: auto;
}

.content-item {
  height: 20px;
  margin-bottom: 5px;
  background-color: white;
}

.footer {
  grid-area: footer;
  background-color: rgba(255, 0, 255, 0.3);
}
<div class="container">
  <div class="header">header</div>
  <div class="side">side</div>
  <div class="content">

    <div class="container container__inner">
      <div class="header header__inner">header</div>
      <div class="side side__inner">side</div>
      <div class="content content__inner overflow">
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
        <div class="content-item"></div>
      </div>
      <div class="footer footer__inner">footer</div>
    </div>

  </div>
  <div class="footer">footer</div>
</div>


Solution

  • The problem is easy to describe, but may not have a simple solution (depending on your requirements).

    The problem is actually clear to see in the title itself:

    Css grid auto sized row content with overflow

    Auto-sized elements cannot overflow. They are auto sized. This means the element shrinks and expands based on its content size, never having a need to overflow.

    There has to be something that sets a fixed length on the container. A boundary that can be crossed, thus triggering an overflow condition.

    From 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.

    Your row heights are insufficient to trigger an overflow:

    grid-template-rows: min-content minmax(min-content, max-content) min-content
    

    But something like this would work:

      grid-template-rows: 10% 80% 10%
    

    Unfortunately, the code above only works in Chrome. For the layout to also work in Firefox and Edge, do this:

      grid-template-rows: 10% minmax(0, 80%) 10%
    

    body {
      display: block;
      height: 100vh;
      width: 100%;
      background-color: white;
      margin: 0;
    }
    
    .container {
      max-height: 100%;
      display: grid;
      grid-template-areas: "header header" "side content" "footer footer";
      grid-template-columns: 50px auto;
      grid-template-rows: 10% minmax(0, 80%) 10%; /* adjustment */
    }
    
    .header {
      grid-area: header;
      background-color: rgba(255, 255, 0, 0.3);
    }
    
    .side {
      grid-area: side;
      background-color: rgba(0, 255, 0, 0.3);
    }
    
    .content {
      grid-area: content;
      background-color: rgba(0, 0, 255, 0.3);
    }
    
    .overflow {
      overflow: auto;
    }
    
    .content-item {
      height: 20px;
      margin-bottom: 5px;
      background-color: white;
    }
    
    .footer {
      grid-area: footer;
      background-color: rgba(255, 0, 255, 0.3);
    }
    <div class="container">
      <div class="header">header</div>
      <div class="side">side</div>
      <div class="content">
        <div class="container container__inner">
          <div class="header header__inner">header</div>
          <div class="side side__inner">side</div>
          <div class="content content__inner overflow">
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
            <div class="content-item"></div>
          </div>
          <div class="footer footer__inner">footer</div>
        </div>
      </div>
      <div class="footer">footer</div>
    </div>

    Basically, you need to find some way to set a fixed height limit on the overflow row for the scrollbar to function.


    For an explanation of the browser differences described above see these posts: