Flex shrink in nested containers - html

I am trying to mimic a nav bar with flex box where i have 2 sets of items - nav-left aligned to the left and nav-right aligned to the right. Each item has 50px width.
nav-right should take the width of the children. nav-left should take up the rest of the space.
https://jsbin.com/xanaxaruxo/edit?html,css,output
.nav-group {
display: flex;
flex-direction: row;
border: 2px solid;
padding: 2px;
}
.nav-group-left {
border-color: turquoise;
flex: 1 1 auto;
}
.nav-group-right {
border-color: lime;
flex: 0 1 auto;
}
My expectation is this -
The nav-left container should take the available space but when i shrink, this space should shrink first but when total is more than the container- all the elements should shrink together.
I have put flex: 1 1 auto - but the elements are overflowing the container;
(2nd bar)
But it works when the flex containers are not nested.
https://jsbin.com/bapaqecabe/edit?html,css,output
My question is why does not shrink work with nested flex containers.

It doesn't work when the items are nested because they have a defined width. Their parents have flex basis of auto and shrink set to true, they do what they're supposed to, but their children will expand them regardless.
If you require your navigation items to have a static width, one solution I see being used a lot is to have them show as a list on smaller devices.
If you're fine with them shrinking a bit, you could make them flexible, set their basis to 50px, and modify both containers to grow, but the left one more than the right one, say 3 to 1.
.nav-group-left {
border-color: turquoise;
flex: 3 1 auto;
}
.nav-group-right {
border-color: lime;
flex: 1 1 auto;
}
.item {
border: 3px solid;
display: flex;
flex-basis: 50px;
}
Example

An explicit width will override flex-grow and flex-shrink. For flex-grow and flex-shrink to work their direct parent must be set to display: flex;. Their widths will be rendered relative to the width of their content.
<div style="display: flex;">
<div style="flex-grow: 1;"><p>Div should use up remaining space.</p></div>
<div style="flex-shrink: 1;"><p>Div should shrink to minimal space.</div>
</div>
Use flex-grow when you have an element (typically display: inline;) to use up the rest of the space (e.g. an input that you use for meta descriptions).
Use flex-shrink for elements (typically display: block;) that you do not want using up more than it's equal share of space.

Related

Why is Firefox not honoring flexed div's height, but Chrome is?

This is best illustrated with a simple example.
I have a container with display: flex and flex-direction: column, with a single div inside with height: 300px and flex: 1.
Chrome renders the nested div at 300px tall, but Firefox renders it as a single line. Is this just a nuance between the implementation of flexbox between the two browsers, or is this bad code somehow? If a nuance, what's the best way to mitigate?
.container {
display: flex;
flex-direction: column;
}
.container > div {
background-color: #666;
color: white;
flex: 1;
height: 300px;
}
<div class="container">
<div>Single line in Firefox, but 300px tall in Chrome!</div>
</div>
The flex: 1 shorthand rule breaks down as follows:
flex-grow: 1
flex-shrink: 1
flex-basis: 0
Chrome sees this, but overrides flex-basis with height: 300px.
Firefox sees this, but does not override flex-basis with height: 300px.
The simple cross-browser solution is to get rid of the height rule and just use:
flex: 1 0 300px
In terms of the spec, Firefox has the correct behavior:
7.1. The flex
Shorthand
When a box is a flex item, flex is consulted instead of the main
size property to determine the main size of the box.
The flex item’s main size
property is
either the width or height property.

Why do flex items wrap instead of shrink?

I wonder if anyone could give me a simple intro about how flexbox layout gets calculated, especially in which priority order, for example:
<div id="container" style="display: flex; flex-direction: row; flex-wrap:wrap;">
<div style="flex: 1 0 200px; height:200px; background-color: lightgreen;"></div>
<div style="flex: 1 1 400px; height:200px; background-color: lightyellow;"></div>
<div style="flex: 1 0 200px; height:200px; background-color: lightblue;"></div>
</div>
I find it interesting that if I make the container width smaller than 600px; the wrap will take effect first before the shrinking (I set the second yellow block to flex: 1 1 400px;) which turn into 3 rows, then the shrinking take effect until container reach 200px;
I wonder what is the order/rule the flexbox layout get decided? Why it does not just shrink from 400 block?
And even I also set all three blocks to flex: 1 1 their basis size; the wrap still happens first.
Thanks
See Flex Layout Algorithm in the spec:
Initial Setup
Line Length Determination
Main Size Determination
Cross Size Determination
Main-Axis Alignment
Cross-Axis Alignment
Resolving Flexible Lengths
Definite and Indefinite Sizes
Intrinsic Sizes
First, at step 3, flex items are collected into flex lines.
Later, at step 7, they flex (grow, shrink, or remain unaltered).
So yes, flex-wrap, which determines how flex items are collected into flex lines, has more "priority" than flex-shrink.
The flex-shrink property often comes in handy in a single-line flex container (flex-wrap: nowrap).
In other words, when the flex items cannot wrap, flex-shrink will enable them to shrink when the container is too small (the spec calls this negative free space). This has the effect of preventing flex items from overflowing the container.
However, in a multi-line flex container (flex-wrap: wrap) no negative free space is created most of the time because flex items can create additional lines. An exception is when a multi-line container in, let's say, row-direction, is narrow enough to wrap all items. This leaves items stacked in a single column. Now they will shrink or hold firm, depending on flex-shrink (demo).
You have a row-direction, wrap-enabled flex container with three flex items.
div #1 ~ flex: 1 0 200px
div #2 ~ flex: 1 1 400px
div #3 ~ flex: 1 0 200px
When the container is greater than 800px, all items stay on the line.
When the container breaks below 800px, div #3 will wrap (there is no need to shrink, even if it could).
At the 600px mark (the combined width of divs #1 & #2), div #2 wraps. Again, no need to shrink.
#container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
div:nth-child(1) {
flex: 1 0 200px;
height: 200px;
background-color: lightgreen;
}
div:nth-child(2) {
flex: 1 1 400px;
height: 200px;
background-color: lightyellow;
}
div:nth-child(3) {
flex: 1 0 200px;
height: 200px;
background-color: lightblue;
}
<div id="container">
<div></div>
<div></div>
<div></div>
</div>
From the spec:
flex-shrink
[This property] specifies the flex shrink factor, which determines how
much the flex item will shrink relative to the rest of the flex items
in the flex container when negative free space is distributed.

Images in flex items causing equal width to break

The red borders are around flex items. They have
flex-grow: 1
These items are contained in a flex container:
width: 100%
display: flex
flex-direction: row
align-items: flex-end
As you can see the vertical alignment is working fine.
When I put the images in, the width of the images pushes the flex items to be bigger or smaller than they should be. The width of the images overrides the width of the flex items. What I want to say is something like:
Firstly make the flex items all the same size and then size the contained images (whatever their natural size) to be the size of their container (the flex item). I've tried width: 100% and width: auto on the images. Didn't help.
This image shows the equally spaced flex items (red borders). I want the images to fit into these boxes without causing the width of the box to change. This is the behaviour I get if I replace the flex items with table cells.
This Fiddle shows the 3 equal boxes: https://jsfiddle.net/justinwyllie/zdrd89gu/
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
flex-grow: 1;
border: 2px solid red;
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">B</div>
<div class="flex-boxes">C</div>
</div>
This one shows an image in the middle flex item. It has completely messed up the equal boxes. The question is what do I need to do to make the cat fit into the middle box? (Fit width-wise that is; i don't mind about height).
https://jsfiddle.net/justinwyllie/zdrd89gu/1/
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
flex-grow: 1;
border: 2px solid red;
}
.cat {
width: auto;
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">
<img class="cat" src="http://www.publicdomainpictures.net/pictures/30000/velka/annoyed-cat.jpg" />
</div>
<div class="flex-boxes">C</div>
</div>
The flex-grow property tells flex items how much available space in the container to consume.
In your second image, the empty items are all equal width because flex-grow: 1 tells them to distribute available space equally among themselves.
Any content you put in these items will impact flex-grow since, as mentioned, flex-grow is based on the free space in the container – and content consumes space.
If you want the items to maintain a fixed width, use flex-basis.
So instead of:
flex-grow: 1;
Try:
flex: 0 0 20%; /* don't grow, don't shrink, fixed width at 20% */
The 20% is just an example, if you wanted five items per row.
Here's your revised code:
jsFiddle
.flex-container {
display: flex;
flex-direction: row;
align-items: flex-end;
}
.flex-boxes {
/* flex-grow: 1; <-- REMOVE */
flex: 0 0 33.33%; /* NEW */
border: 2px solid red;
/* box-sizing: border-box; you may need to add this property if
you enable wrapping */
}
.cat {
width: auto;
/* vertical-align: bottom; add this property if you want to remove
the small gap of whitespace underneath images
https://stackoverflow.com/a/31445364/3597276 */
}
<div class="flex-container">
<div class="flex-boxes">A</div>
<div class="flex-boxes">
<img class="cat" src="http://www.publicdomainpictures.net/pictures/30000/velka/annoyed-cat.jpg" />
</div>
<div class="flex-boxes">C</div>
</div>
Learn more here:
flex-grow not sizing flex items as expected

How exacty does the display: flex property work on a container div, and the flex property work on a div child?

I am trying to understand exactly how the display: flex property works in this layout: http://www.html.it/demo/javascript/55905/demo_index.html
As you can see in this layout there is an external div container having id="com" and that have this CSS:
.com {
align-items: stretch;
display: flex;
height: 100vh;
overflow: hidden;
}
So it have set the display: flex; coupled with the align-items: stretch; properties. So, reading here:
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
It seems to me that it is defining a container in which the element are put one next to the other, but I am absolutely not sure if I am missing something.
Then, inside this container that defined the content having id="com__content", this one:
<div class="com__content">
that have the following CSS settings associated:
.com__content {
background: #404855 none repeat scroll 0 0;
flex: 8 1 0;
padding: 10vh 10vw;
position: relative;
}
Also this internal component have something related to the flex behavior.
What exactly does the flex: 8 1 0; property on this div mean?
flex: 8 1 0;
is shortcut stand for 'flex-grow', 'flex-shrink' and 'flex-basis'
first flex-grow 8 means: this div totally have 8/ total space
for exmple:
you have a div.first flex-grow: 1;
and another div.second flex-grow: 2;
the first div have only 1/3 psace in this line, and left over would be second div's place.
flex-shrink
This defines the ability for a flex item to shrink if necessary.
value 1 mean shrink 1 times
last flex-basis
defines the default size of an element before the remaining space is distributed.
you can setup as It can be a length(20% or 100px), it specifies the initial size of the flex item
as your mentions that article A Complete Guide to Flexbox had metion
flex
This is the shorthand for flex-grow, flex-shrink and flex-basis combined. The second and third parameters (flex-shrink and flex-basis) are optional. Default is 0 1 auto.

In a flexbox layout, is there a `flex-shrink: 0` declaration for the cross axis?

I want to prevent elements in a flex container from shrinking in the dimension that is not the flex-direction. The following example has <article> elements side by side in a row. When the available vertical space is reduced, these elements do not force their flex container to display a scrollbar; instead the content overflows the element boundary.
Screenshot 1 - there is enough horizontal and vertical space to display everything:
Screenshot 2 - the reduced vertical space pushes the element border up:
Screenshot 3 - vertical space further reduced, container finally gets a scrollbar:
Screenshot 4 - without flex-shrink:0, the element widths (main flex axis) will also be reduced:
flex-shrink:0 can prevent horizontal shrinking, but how can I prevent the elements from shrinking vertically?
Giving the <article> elements overflow: auto or something similar does not give the desired result (= scrollbar on the container). Ideally, the display would look like this montage:
If I knew the elements' height in advance, I could give them a min-height, but that is not always the case.
FIDDLE: http://jsfiddle.net/twdan8u8/
HTML:
<main>
<article>article<br>article<br>article</article>
<article>article<br>article<br>article</article>
</main>
CSS:
* {
box-sizing: border-box; /* not the culprit */
}
html {
height: 100%;
}
body {
position: relative;
height: 100%;
margin: 0;
background: #999;
}
main {
overflow: auto;
background: gold;
display: flex;
height: 80%;
padding: 50px 30px;
}
article {
flex-shrink: 0;
font-size: 28px;
border: 2px solid red;
margin-right: 30px;
padding: 10px;
}
As is so often the case, I found the (or rather a) solution just when I finished writing the question. Since this might help somebody else, here's what I found out:
If the flex container is given the style align-items: flex-start, element heights are not reduced and the container gets a scrollbar when necessary (assuming a suitable overflow value).
The default for this property is "stretch". It can also be set on individual flex elements using align-self. The drawback is that the elements are now no longer equally high (i.e., they don't stretch to the full available height anymore).