1

I want a flexbox container that has a maximum height (say 50% of its container). Pretty simple right?

.container {
  display: flex;
  flex-direction: column;
  border: 1px solid red;
  overflow: hidden;
  height: 50%;
  position: absolute;
}
.list {
  flex: 0 1 auto;
  overflow: auto;
}
.fixed {
  flex: 0 0 auto;
}
<div class="container">
    <header class="fixed">Header</header>
    <div class="list">
        <ul>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
        </ul>
    </div>
    <footer class="fixed">Footer</footer>
</div>

That works, but remove the position: absolute and grows bigger than 50% (and the width changes). Why is that?

.container {
  display: flex;
  flex-direction: column;
  border: 1px solid red;
  overflow: hidden;
  height: 50%;
}
.list {
  flex: 0 1 auto;
  overflow: auto;
}
.fixed {
  flex: 0 0 auto;
}
<div class="container">
    <header class="fixed">Header</header>
    <div class="list">
        <ul>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
            <li>Item</li>
        </ul>
    </div>
    <footer class="fixed">Footer</footer>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Timmmm
  • 88,195
  • 71
  • 364
  • 509

1 Answers1

1

The behavior is defined in the specification:

10.5 Content height: the height property

<percentage>

Specifies a percentage height.

The percentage is calculated with respect to the height of the generated box's containing block.

If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the used height is calculated as if auto was specified.

So, here are two takeaways:

  1. When your container is not absolutely positioned (i.e., it remains in-flow), the fact that there is no height defined on the parent means that height: 50% resolves to height: auto (height of the content).

    If you set, let's say, body { height: 100vh }, your container will take 50% height.

body {
  height: 100vh;
}

.container {
  display: flex;
  flex-direction: column;
  border: 1px solid red;
  overflow: hidden;
  height: 50%;
}

.list {
  flex: 0 1 auto;
  overflow: auto;
}

.fixed {
  flex: 0 0 auto;
}
<div class="container">
  <header class="fixed">Header</header>
  <div class="list">
    <ul>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
    </ul>
  </div>
  <footer class="fixed">Footer</footer>
</div>
  1. When your container is absolutely positioned, the height: auto rule doesn't apply and height: 50% works as intended.

    Also, in-flow block level elements take the width of their container. Once you apply absolute positioning -- removing the element from the document flow -- the "take the width of the parent" rule no longer applies, and you need to specify the width of the container, or define the offset properties (i.e., left, right, etc.).

.container {
  display: flex;
  flex-direction: column;
  border: 1px solid red;
  overflow: hidden;
  height: 50%;
  position: absolute;
  
  /* new */
  left: 0;
  right: 0;
  /* or this */
  /* width: 100%; */
}

.list {
  flex: 0 1 auto;
  overflow: auto;
}

.fixed {
  flex: 0 0 auto;
}

/* new */
 * { box-sizing: border-box; }
<div class="container">
  <header class="fixed">Header</header>
  <div class="list">
    <ul>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
      <li>Item</li>
    </ul>
  </div>
  <footer class="fixed">Footer</footer>
</div>

More details:

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701