Percentage Height Not Working in Nested Flexbox Layout in Chrome

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

Nested flexbox equal height columns broken in Google Chrome

Checkout my codepen: http://codepen.io/slawaEremin/pen/wBGBrj

The main idea is to use display: flex for the direct child (.l-sidebar) of the parent block (.l-container) and to use flex-basis: 100% instead of height: 100% in .l-sidebar-inner because Chrome doesn't understand the height: 100%;

.l-sidebar {
display: flex;
}
.l-sidebar-inner {
flex-basis: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
}

Chrome / Safari not filling 100% height of flex parent

Solution

Use nested flex containers.

Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.

Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.

Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).

Try this (no changes to HTML):

.container {

display: flex;

flex-direction: column;

height: 20em;

border: 5px solid black

}

.item {

display: flex; /* new; nested flex container */

flex: 1;

border-bottom: 1px solid white;

}

.item-inner {

display: flex; /* new; nested flex container */

flex: 1; /* new */

/* height: 100%; <-- remove; unnecessary */

/* width: 100%; <-- remove; unnecessary */

/* display: table; <-- remove; unnecessary */

}

a {

display: flex; /* new; nested flex container */

flex: 1; /* new */

align-items: center; /* new; vertically center text */

background: orange;

/* display: table-cell; <-- remove; unnecessary */

/* vertical-align: middle; <-- remove; unnecessary */

}
<div class="container">

<div class="item">

<div class="item-inner">

<a>Button</a>

</div>

</div>

<div class="item">

<div class="item-inner">

<a>Button</a>

</div>

</div>

<div class="item">

<div class="item-inner">

<a>Button</a>

</div>

</div>

</div>

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

Flexbox in Chrome--How to limit size of nested elements?

First, let's tackle the terminology:

...how do I force an element nested inside of a member of a flexbox (by "member of a flexbox" I mean a child of an element styled with display:flex) to limit its size to the size of the flexbox member it's nested under?

An element with display: flex is called a flex container (technically) or flex parent (colloquially).

The children of a flex container are called flex items. Note the word children (first-level). Descendents of a flex container beyond the children are not flex items and most flex properties don't apply to them.


Now, addressing your question:

The problem is that Firefox (and apparently IE11) have a different interpretation of the percentage height rule than Chrome.

Specifically, the vertical scrollbar you want is not rendering in Chrome because you're using percentage heights in a way that doesn't conform with the traditional implementation of the spec.

CSS 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 and this element is not absolutely positioned, the value computes to "auto".

auto
The height depends on the values of other properties.

In other words, if you want an element to have a percentage height, then you must specify a height on the containing block (i.e. the parent).

In your code, body (level 1) has height: 100vh.

One level down, .max-flex (level 2) has height: 100%.

Four levels down, .large-nested-div (level 4) has height: 100%.

However, at .variable-flex-content (level 3), there is no height property. You are defining the height with flex: 1 1 0. As far as Chrome is concerned, this is a missing link in the chain and a violation of the spec. Chrome is expecting to see the height property, and when it doesn't, it computes the height to auto.


Chrome vs Firefox (I haven't tested IE11, so I won't mention it here)

Traditionally, when calculating percentage heights, browsers have interpreted the spec's use of the term "height" to mean the value of the height property. It could just as easily be interpreted as a height (generic term), but the height property requirement has become the predominant implementation. I've never seen min-height or max-height work on a parent when dealing with percentage heights.

Recently, however, as noted in this question and another one and another one, Firefox has broadened its interpretation to accept flex heights, as well.

It's not clear which browser is more compliant.

It doesn't help matters that the height property definition hasn't been updated since 1998 (CSS2).


The Solution

Instead of defining the height of .variable-flex-content with flex: 1 1 0%, try using height: 100% or height: calc(100% - 60px) or absolute positioning.

How to avoid Chrome bug with zero height nested flexbox?

You may just use the shorthand flex:1; to make it simple(and avoid different behavior from browser to browser) since the container is suppose to be filling whatever room is left.

https://jsfiddle.net/4co25fau/2/

For safety, i would mind a min-height on the main container to avoid overflow, overlap and to shrink down to 0 some of the containers.

@RahatAhmed wrote: To clarify, flex: 1 also sets flex-basis: 100% as a default, which is the specific rule that fixes the issue

Height is not correct in flexbox items in Chrome

You could absolutely position div id="half_of_content".

#content {
flex: 1;
height: 100%;
background-color: green;
position: relative; /* new */
}

#half_of_content {
height: 50%;
background-color: yellow;
position: absolute; /* new */
width: 100%; /* new */
}

DEMO

With regard to your statement:

But it seems like if Chrome treats it like half of the whole page
rather than the flex item.

You gave the body a height: 100%. Then gave its child (.wrapper) a height: 100%. Then gave its child (.content) a height: 100%. So they're all equal height. Giving the next child (#half_of_content) a height: 50% would naturally be 50% height of body.

With absolute positioning, however, you don't need to specify parent heights.

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>

100% height when parent is flex item: Chrome bug?

It's because you are just using flex-grow:1 on the #middle div.

If you use the recommended shorthand of flex:1...it works perfectly.

#outer {

display: flex;

flex-direction: column;

background-color: white;

height: 300px;

}

#middle {

flex: 1;

/* here */

background-color: beige;

}

#inner {

background-color: black;

height: 100%;

color: white;

}
<div id="outer">

<div id="middle">

<div id="inner">

x

</div>

</div>

</div>


Related Topics



Leave a reply



Submit