Search code examples
htmlcsslayoutflexbox

Nested full-height CSS flexbox columns, scrolling behavior


I'm trying to create a full-height layout where only the bottom "part" of the layout scrolls. That is, a couple fixed title bars and then the whole area below scrolls.

Could be done with tables or absolute positioning, but seems like flexbox is the modern answer. And it seems to work fine with a flat column flexbox with three divs and flex: 1; overflow: auto in the last div. But not when flexboxes are nested. When I use nested flexboxes, I always get a scrollbar on the whole page, never a scrollbar in the main content area.

This flat flexbox layout works as intended:

outer
    b1
    b2
    b3

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
}

.outer {
  background: white;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.b1 {
  background: lightblue;
}

.b2 {
  background: pink;
}

.b3 {
  flex: 1;
  background: green;
  overflow: auto;
}
<div class='outer'>
  <div class='b1'>
    b1
  </div>
  <div class='b2'>
    b2
  </div>
  <div class='b3'>
    b3<br/> b3
    <br/> b3
    <br/> b3
    <br/> b3
    <br/> b3
    <br/> b3
    <br/> b3
    <br/> b3
    <br/> b3
    <br/> b3
    <br/>
  </div>
</div>

This nested structure doesn't work as intended:

outer
    a1
    a2
        b1
        b2

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
  width: 100%;
}

.outer {
  background: white;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
}

.a1 {
  background: lightblue;
}

.a2 {
  flex: 1;
  background: green;
  display: flex;
  flex-direction: column;
}

.b1 {
  background: pink;
}

.b2 {
  flex: 1;
  background: yellow;
  overflow: auto;
}
<div class='outer'>
  <div class='a1'>
    a1
  </div>
  <div class='a2'>
    <div class='b1'>
      b1
    </div>
    <div class='b2'>
      b2<br/> b2
      <br/> b2
      <br/> b2
      <br/> b2
      <br/> b2
      <br/> b2
      <br/> b2
      <br/> b2
      <br/> b2
      <br/> b2
      <br/>
    </div>
  </div>
</div>


Solution

  • Your b2 is doing exactly what you want, it's just flowing out of the bottom of a2. Add overflow: hidden to a2 to stop this behaviour:

    .a2 {
      overflow: hidden;
    }
    

    html,
    body {
      margin: 0;
      height: 100%;
    }
    
    .outer {
      background: white;
      height: 100%;
      display: flex;
      flex-direction: column;
    }
    
    .a1 {
      background: lightblue;
    }
    
    .a2 {
      flex: 1;
      background: green;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }
    
    .b1 {
      background: pink;
    }
    
    .b2 {
      flex: 1;
      background: yellow;
      overflow: auto;
    }
    <div class='outer'>
      <div class='a1'>
        a1
      </div>
      <div class='a2'>
        <div class='b1'>
          b1
        </div>
        <div class='b2'>
          b2<br/> b2
          <br/> b2
          <br/> b2
          <br/> b2
          <br/> b2
          <br/> b2
          <br/> b2
          <br/> b2
          <br/> b2
          <br/> b2
          <br/>
        </div>
      </div>
    </div>