Percentage Height in Nested Flex Box

percentage height in nested flex box

You need to add a height to your right column:

http://jsfiddle.net/59trW/2/

.flex {
display: flex;
flex:1;
flex-direction: column;
background-color: #64b92a;
height: 100%; /* here */
}

Also, -webkit-box-align: stretch is doing nothing for you because it comes from the old 2009 draft (which you aren't even using here), not the current spec (also, stretch is the default value for that property).

Percentage height not working in nested flexbox layout in Chrome

The question says:

I have a following layout fully working in Firefox and IE.
Unfortunately it is broken in Chrome, namely the dark blue
container is collapsed even though it has height 100% of its parent.

Actually, an argument could be made that the opposite is true: Chrome has it right, while Firefox and IE are "broken".

First, here's the solution:

.item3 { height: 100%; }

Now let's look at your document structure and the heights applied:

<html> <!-- height: 97% -->
<body> <!-- height: 97% -->
<div class="container"> <!-- height: 100%; -->
<div class="item3"> <!-- height: ?? -->
<div class="container column c2"> <!-- height: 100% ; this is the collapsed box -->
...
...
...

As per the CSS specification, when using percentages to set the height of an element (like you are doing with .container), the parent element must also have an explicit height. In reference to your collapsed div, its parent (.item3) does not have a defined height.

From the spec:

<percentage>
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'.

auto
The height depends on the values of other properties.

In terms of the height property, it would appear from this example that Chrome defines "containing block" as "parent", while Firefox and IE define "containing block" as "ancestor", or they respect flex heights as a reference for percentage heights.

Hence, since the div with the dark blue border (.container column c2) has no content, and its parent has no specified height, then there is no height and the box collapses in Chrome.

However, by specifying a height for .item3, which is the parent of the collapsed box, the height works on all browsers.

DEMO


UPDATE

More details:

  • Heights rendering differently in Chrome and Firefox

How can I make Flexbox children 100% height of their parent?

Use align-items: stretch

Similar to David Storey's answer, my workaround is:

.flex-2 {
display: flex;
align-items: stretch;
}

Note that height: 100% should be removed from the child component (see comments).

Alternatively to align-items, you can use align-self just on the .flex-2-child item you want stretched.

nested flexbox height vs max-height

Your code is working as expected when setting the big height because max-height:100% in the child element need a reference, if you remove the height the max-height will fail to auto. As a side note, Firefox will have the same output even if you remove max-height so the issue isn't related to max-height. 1

Instead, you can keep the cascading flex container and rely on the default stretch alignment to obtain what you want:

html, body {  height: 100%;  margin: 0;  padding: 0;}
* { box-sizing: border-box;}
div { padding: 10px;}
.container { display: flex; flex-direction: column;}
.content { flex: 1; max-height: 100%; overflow: auto;}
.root.container { background-color: red; max-height: 100%;}
.sub.container { background-color: purple; width:100%; /*added this*/}
.root.header { background-color: lightblue;}
.sub.header { background-color: lightgreen;}
.root.content { background-color: yellow; display:flex; /*added this*/}
.sub.content { background-color: orange;}
<div class="root container">  <div class="root header">header</div>  <div class="root content">    <div class="sub container">      <div class="sub header">menu</div>      <div class="sub content">        <ul>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>          <li>Item 1</li>          <li>Item 2</li>          <li>Item 3</li>          <li>Item 4</li>          <li>Item 5</li>        </ul>      </div>    </div>  </div></div>

FlexBox - Nested div won't fill it's parent when given a height of 100%

.main {
flex-grow: 1;
background-color: red;
display: flex;
}

.main-child {
flex: 1;
width: 100%;
background-color: yellow;
}

Percentage 'min-height' works only when element has indirect parent with 'display: flex'

You are facing the result of the stretch alignment related to flexbox. by default flex items are stretched so the following apply:

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. ref

For this reason, min-height with percentage is working. If you change the alignment and keep display:flex, it won't work

#main {
display: flex
}

#wrapper {
background-color: violet;
align-self:flex-start;
}

.content {
font-size: 2em
}

#inner {
min-height: 50%;
background-color: green
}
<div id="main">
<div id="wrapper">
<div class="content">Lorem Ipsum is simply dummy text of the printing and typesetting industry. </div>
<div class="content">It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</div>
<div class="content">It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged.</div>
<div id="inner"></div>
</div>
</div>

Flexbox / Expanding height due to nested element

Update
As I already said in the comment. You cannot change the height of the elements independently they are all in a parent-child-relationship.

You should consider using less markup and maybe use a different approach to achieve the same. If it is about the styling you could make .value growing with the .slider-val-text and use an independent absolute element to draw over the .slider-val-text (you just have to add enough padding and draw over it at the top there for this greenish box background).

However, you can set a fixed height to the .value container. Try height: 168px. The problem with that is, that it will not change depending on the text you put in in .slider-val-text.

You could use javascript and set the height of the .value container based on the height of the .slider-val-text once the DOM has finished loading.

document.addEventListener("DOMContentLoaded", function() {
const childHeight = document.querySelector('.slider-val-text').offsetHeight;
const parent = document.querySelector('.value');
let verticalPadding = parseInt(window.getComputedStyle(parent, null).getPropertyValue('padding-top'));
verticalPadding += parseInt(window.getComputedStyle(parent, null).getPropertyValue('padding-bottom'));
parent.style.height = childHeight + verticalPadding + 'px';
});
.menu-item-list {
position: absolute;
top: 2em;
left: 1em;
right: 40%;
}

.menu-item-list li {
display: flex;
}

.menu-item-list .control-slider .value {
box-sizing: border-box;
/* ADDED */
padding: 0 1em 0 0;
}

.menu-item-list .control-slider .slider-view {
width: 100%;
padding: 0;
display: flex;
}

.menu-item-list .control-slider .slider-view .slider-val-line-parent {
padding: 0;
text-align: right;
display: flex;
flex-flow: column wrap;
/*position: absolute; REMOVED */
left: 0;
}

.menu-item-list .control-slider .slider-view .slider-val-line-parent .slider-val-text {
flex: 1;
/* width: calc(100% + 16px); REMOVED */
/*right: 7px;
position: relative;
top: 36px; REMOVED */
padding-top: 36px;
padding-right: 0px;
padding-left: 0px;
font-size: 14px;
justify-content: right;
}

.menu-item-list .control-slider .slider-box {
height: 1.5em;
padding: 0.1em 0.25em;
width: 50%;
position: relative;
background-color: purple;
opacity: 50%;
}

.menu-item-list .control-slider .slider-box .safe-slider {
background-color: green;
margin: 0;
padding: 0;
width: 100%;
height: 100%;
position: relative;
}

.menu-item-list .control-slider .slider-box .safe-slider .slider-val-line-parent {
border-style: dashed;
border-color: black;
display: flex;
}

.menu-item-list .menu-item {
background-color: orange;
}

.menu-item-list .menu-item .label {
width: 57%;
background-color: yellow;
opacity: 50%;
}

.menu-item-list .menu-item .value {
width: 43%;
}

.menu-item-list .menu-item.active.control-slider {
display: flex;
flex-direction: column;
}

.menu-item-list .menu-item.active.control-slider .label {
width: 100%;
margin: 0;
}

.menu-item-list .menu-item.active.control-slider .value {
width: 100%;
padding: 1em 1em 1.5em;
}
<ol class="menu-item-list">
<li id="menu-item-las-powr" class="menu-item control-slider active">
<div class="label">Label</div>
<div class="value">
<div class="slider-view">
<div class="slider-box">
<div class="safe-slider">
<div class="slider-val-line-parent">
<div class="slider-val-text">
BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO
BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO BOO
</div>
</div>
</div>
</div>
</div>
</div>
</li>
</ol>

Prevent flex item from exceeding parent height and make scroll bar work

Short Answer

Instead of flex: 1, use flex: 1 1 1px.

Make these two adjustments in your code:

#messagelist {
/* flex:1; */
flex: 1 1 1px; /* new */
}

#messagecontents {
/* flex:1; */
flex: 1 1 1px; /* new */
}

revised codepen


Explanation

In most cases, as you have noted, adding min-height: 0 to flex items in a column-direction container is enough to correct the problem.

In this case, however, there's an additional obstacle: flex-basis.

You're applying the following rule to flex items #messagelist and #messagecontents: flex: 1.

This is a shorthand rule that breaks down to:

  • flex-grow: 1
  • flex-shrink: 1
  • flex-basis: 0

(source: https://www.w3.org/TR/css-flexbox-1/#flex-common)


2019 UPDATE: Since the posting of this answer in 2018, it appears that Chrome's behavior has changed and is now uniform with Firefox and Edge. Please keep that in mind as you read the rest of this answer.


In Chrome, flex-basis: 0 is enough to trigger an overflow, which generates the scrollbars. (2019 update: This may no longer be the case.)

In Firefox and Edge, however, a zero flex-basis is insufficient. This is probably the more correct behavior in terms of standards compliance as MDN states:

In order for overflow to have an effect, the block-level container must have either a set height (height or max-height) or white-space set to nowrap.

Well, flex-basis: 0 meets none of those conditions, so an overflow condition should not occur. Chrome has probably engaged in an intervention (as they often do).

An intervention is when a user agent decides to deviate slightly from a standardized behavior in order to provide a greatly enhanced user experience.

To meet the "standardized behavior", which would enable an overflow to occur in Firefox and Edge, give flex-basis a fixed height (even if it's just 1px).



Related Topics



Leave a reply



Submit