Does CSS "max-height" Only Work 1 Level Deep? How to Automatically Scroll Nested Web Layouts if so?

Viewed 90

I'm struggling with what seems should very much be a beginner task in CSS: scrolling and heights (when elements are nested more than 1 level deep).

I'd like to design layouts where default elements do not expand past their parent elements, and if they do, overflow: auto kicks in and they start scrolling.

I don't want to set height; 100% on every element though, as I need elements to only take up the space required, and so have been trying to use instead max-height: 100% or max-height: inherit on every element.

When using height: 100%, the height of the parent is correctly picked up even when elements are nested several layers deep, as seen here: Code Pen 1

html {
  padding: 0;
  margin: 0;
  overflow: hidden;
}

body {
  height: 95vh;
  background-color: red;
  overflow: hidden;
}

.levelOne {
  background-color: blue;
  height: 100%;
  /* Correctly gets parent height */
}

.levelTwo {
  background-color: green;
  overflow: auto;
  height: 100%;
  /* Correctly gets grand-parent height */
}
<div class="levelOne">
  <div class=levelTwo>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
  </div>
</div>

When attempting to not overflow a nested div with max-height: 100% however, it appears that the level 1 child correctly stops at the parent height, but a level 2 child will spill out. Code Pen 2

*,
*:before,
*:after {
  box-sizing: inherit;
  max-height: inherit;
  overflow: hidden;
}

html {
  padding: 0;
  margin: 0;
  overflow: hidden;
}

body {
  height: 95vh;
  background-color: red;
  overflow: hidden;
}

.levelOne {
  background-color: blue;
  max-height: 100%;
  /* Correctly gets parent height*/
}

.levelTwo {
  background-color: green;
  overflow: auto;
  max-height: 100%;
  /* DOESN'T get grandparent hight, but spills out of levelOne so overflow: auto never starts to scroll; */
}
<div class="levelOne">
  <div class=levelTwo>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
    <p>Lorem</p>
  </div>
</div>

  1. Why does max-height: 100% not top out at the grandparent's height, while `height: 100%' does? I would have expected similar behavior.
  2. Is there a better method to not allow elements to spill out of their parent sizes?

I have also seen a recommendation to do something like the following, but if max-hight isn't able to calculate past the 1st layer deep, it fails too:

*, *:before, *:after {
    box-sizing: inherit;
    max-height: inherit;
    overflow: hidden;
}

It seem like for some reason if the divs are a 1x1 css grid things work more consistently at deeper levels, but sometimes a grid is too much overhead, and I'm hoping to better understand the basics.

Thank you.

1 Answers

I need elements to only take up the space required

Setting widths relative to the parent will not make elements take up only the space required. By default height is set to auto which only makes elements as tall as they need to be.

Stop levelTwo from overflowing levelOne & universally tell all elements to never expand beyond their parent

Decide what your children will do when they are too large to fit within their explicit height parent:

  • Visibly overflow (Default)
  • Hide (Bad practice)
  • Scroll
  1. Why does max-height: 100% not top out at the grandparent's height, while height: 100% does? I would have expected similar behavior.

Why setting height: 100% on each works, but max-height: 100% does not

MDN - 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 percentage value is treated as none.

So because your levelOne container has a max-height and not a height, the max height is seen as 100% of auto - and 100% of "as much as you need" is a max-height of none.

  1. Is there a better method to not allow elements to spill out of their parent sizes?

Please let me know if this example is in the direction you want.

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  height: 95vh;
  background-color: red;
}

.levelOne {
  background-color: blue;
  max-height: 100%;
  /* Correctly gets parent height*/
  overflow: auto;
}

.levelTwo {
  background-color: green;
}
<div class="levelOne">
  <div class=levelTwo>
    <p>Lorem</p>
    <p>Lorem</p>
  </div>
</div>

levelOne will be as big as it needs to be to fit it's content with default CSS height value of auto. It will cap out at 100% of its parent height with the max height and scroll when the content exceeds this because of the overflow: auto.

Related