Why does height: 100% on a child element not apply when the parent element has a min-height/max-height value but no height value?

Say we have the following set up:

.container {
  background-color: red;
  width: 500px;
  min-height: 300px;
}

.child {
  background-color: blue;
  width: 500px;
  height: 100%;
}
<div class="container">
  <div class="child">
    
  </div>
</div>

It's clear that the container element is rendering with a height set to 300px, but the child element has no height whatsoever, despite being set to 100%.

When the height of the container element is set to even 1px, the child element will suddenly fill the entire container with a height of 300px.

.container {
  background-color: red;
  width: 500px;
  min-height: 300px;
  height: 1px;
}

.child {
  background-color: blue;
  width: 500px;
  height: 100%;
}
<div class="container">
  <div class="child">
    
  </div>
</div>

The container element is clearly rendering at 300px even without height being set so why does it require setting the height before the child element actually applies it's height: 100%?

Edit: To be clear, I'm not looking for a solution to having the child height take up the entire parent element, I would just like to understand why this behaves like this.


Solution 1:

In the first case, you don't have any height defined so it's clear that the precentage height on child will fail.

From the specification:

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 value computes to 'auto'.

min-height is only a boundary and the height of your element still depend on its content. If you will have one that exceed 300px the element will have more than 300px

.container {
  background-color: red;
  width: 500px;
  min-height: 300px;
  padding:10px;
}

.child {
  background-color: blue;
  width: 500px;
  height: 400px;
  animation:change 2s linear infinite alternate;
}
@keyframes change{
   from {
     height:100px;
   }
}
<div class="container">
  <div class="child">
    
  </div>
</div>

In the second case you have specified a height so the precentage will work but the height calculation is a bit tricky since we also have min-height

The following algorithm describes how the two properties influence the used value of the 'height' property:

  1. The tentative used height is calculated (without 'min-height' and 'max-height') following the rules under "Calculating heights and margins" above.
  2. If this tentative height is greater than 'max-height', the rules above are applied again, but this time using the value of 'max-height' as the computed value for 'height'.
  3. If the resulting height is smaller than 'min-height', the rules above are applied again, but this time using the value of 'min-height' as the computed value for 'height'.

In the second case, it's like you explicitely defined height:300px and percentage will consider this value. In this situation, even if the content is bigger the parent element will not grow and you will have overflow. You can even define a height equal to 0.

.container {
  background-color: red;
  width: 500px;
  min-height: 300px;
  height: 0;
  padding:10px;
}

.child {
  background-color: blue;
  width: 500px;
  height: 400px; 
  animation:change 2s linear infinite alternate;
}
@keyframes change{
   from {
     height:100px;
   }
}
<div class="container">
  <div class="child">
    
  </div>
</div>

Same logic will happen with max-height but in this case the height need to be very big

.container {
  background-color: red;
  width: 500px;
  max-height: 300px;
  height: 99999px;
  padding:10px;
}

.child {
  background-color: blue;
  width: 500px;
  height: 400px; 
  animation:change 2s linear infinite alternate;
}
@keyframes change{
   from {
     height:100px;
   }
}
<div class="container">
  <div class="child">
    
  </div>
</div>

If you are intrested you can transform your logic using flexbox and you will be able to do what you want without having to set an explicit height.

Relying on the default stretch alignment on the cross-axis

.container {
  background-color: red;
  width: 500px;
  min-height: 300px;
  display:flex;
}

.child {
  background-color: blue;
  width: 500px;
  /* height: 100%;*/
}
<div class="container">
  <div class="child">
    
  </div>
</div>

Or using the flex-grow property instead of height in main axis:

.container {
  background-color: red;
  width: 500px;
  min-height: 300px;
  display:flex;
  flex-direction:column;
}

.child {
  background-color: blue;
  width: 500px;
  flex-grow:0.8; /* this is 80%, use 1 for 100% */
}
<div class="container">
  <div class="child">
    
  </div>
</div>

CSS grid can also handle this

.container {
  background-color: red;
  width: 500px;
  min-height: 300px;
  display:grid;
  grid-template-rows:1fr;
}

.child {
  background-color: blue;
  width: 500px;
  height:80%;
}
<div class="container">
  <div class="child">
    
  </div>
</div>