This is a very contrived example to distill the problem I'm observing. I know I can work around it, but I still want to understand why this doesn't work the way I expect it to. I'd basically like to know: Is it a bug? Or is it behaving as it's designed? If so, what are the specs?
Problem summary: Percentage height
or max-height
is undesirably ignored on an element that is nested within 2 (maybe more) flex boxes that are flex-direction: row
, and align-items
is anything but the default stretch
(flex-end
in the example).
The design has category boxes laid out in a row, each with item boxes, also laid out in rows. I want each category and item to size to its contents, limit its height to its parent, and align to the bottom. If there is overflow, I want the item to scroll its contents.
Here's a Fiddle: https://jsfiddle.net/72Lf6nas/
HTML:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="stylesheet" href="src/style.css" />
</head>
<body>
<div class="container">
<div class="category">
<div class="item">
<h2>Item 1</h2>
<p>Small content in section 1</p>
</div>
</div>
<div class="category">
<div class="item">
<h2>Item 2</h2>
<p>Large content in section 2. This section should scroll.</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed
ultrices mi sit amet laoreet pulvinar. Nam vel pulvinar ligula, sit
amet dictum ligula. Maecenas tincidunt, dui vel sollicitudin
euismod, purus ipsum tincidunt mauris, nec tincidunt dolor odio at
turpis. Aenean non lacus ac purus rhoncus ultrices. Fusce ultrices
iaculis tellus, et venenatis neque ullamcorper tincidunt. Curabitur
non urna vitae odio hendrerit sagittis. Ut vitae accumsan arcu.
</p>
</div>
<div class="item">
<h2>Item 3</h2>
Small content in section 2.
</div>
</div>
</div>
</body>
</html>
CSS:
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
}
body {
height: 100vh;
}
.container {
height: 100%;
display: flex;
/* Comment this out here or in .category to make .item's max-height work. */
align-items: flex-end;
overflow: hidden;
gap: 5px;
padding: 5px;
border: 3px solid red;
}
.category {
display: flex;
flex: 1;
/* Comment this out here or in .container to make .item's max-height work. */
align-items: flex-end;
max-height: 100%;
overflow: hidden;
border: 3px solid green;
gap: 5px;
padding: 5px;
}
.item {
position: relative;
width: 200px;
/* PROBLEM: Not honored when both .container and .category have align-item specified */
max-height: 100%;
overflow-y: auto;
border: 3px solid blue;
padding: 10px;
}
What I expect: Screenshot - Item box that would overflow scrolls instead
What happens: Screenshot - item max-height not obeyed, and overflowed item contents don't scroll
(Sorry, I'm new, not allowed to embed images yet!)
The item box does not obey max-height: 100%
and overflows, despite its parent category displaying obvious boundaries and implying presence of reference height. Setting item's height
to a percentage value also does nothing, but it does respond if either height
or max-height
being set to a pixel value. Responding to absolute values implying only percentage value is ignored.
I thought maybe flex-end
was somehow removing the height reference needed to calculate relative height, but keeping align-items: flex-end
on the direct ancestor of item (category) while removing it from container will make it heed item's max-height
correctly. It only borks when item's parent and grandparent both have align-items: flex-end
specified. Even so, there should still be a derived reference height starting from body's 100vh
, so it's a surprise that the double-nesting would cause this behavior.
Appreciate any help in understanding this!
The concept here is that of a definite height. A percentage height only works if the value of which it's a percentage is definite.
The height of the viewport is definite. It's the height of the browser window as determined by the user.
The height of the body
element is given is viewport units, so its height is also definite.
The height of the .container
element is a percentage of the body
element's height which is definite, so its height too is definite.
The height of the .category
elements is not given. Its align-self
value is flex-end
(adopted from the align-items value of its parent) which is insufficient to make the elements' heights definite.
But if the align-self value it adopts (or specified directly) is "stretch" then that is sufficient to make the elements' heights definite. The Flexbox specification explicitly says so:
If the flex item has align-self: stretch, redo layout for its contents, treating this used size as its definite cross size so that percentage-sized children can be resolved.
This "redo the layout" means that it works both from parent to child and child to parent. If the .item
element's align-self is stretch, then it will be the height of its .category
parent which will, when its align-self is flex-end and thus height determined by its contents, in turn cause it to grow to the height of the .container
element.