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.
Related
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.
I am trying to have a number of items underneath each other in a container with a set height. Items will then carry on next to each other if there's no space left.
This is the idea:
I am trying to achieve this using flexbox, a container with a set height, direction is set to column and flex-wrap is wrap:
The issue is that there are wide gaps between the columns.
I tried setting both justify-content and align-items to flex-start, but that is probably the default value.
Is there any way to solve this?
Here is the code:
* {
box-sizing: border-box;
}
body {
font-family: sans-serif;
}
.container {
display: flex;
flex-wrap: wrap;
height: 300px;
flex-direction: column;
background-color: #ccc;
}
.items {
width: 100px;
height: 100px;
margin: 10px;
background-color: tomato;
color: white;
font-size: 60px;
font-weight: bold;
text-align: center;
padding: 15px;
}
<div class="container">
<div class="items">1</div>
<div class="items">2</div>
<div class="items">3</div>
<div class="items">4</div>
<div class="items">5</div>
</div>
codepen
An initial setting of a flex container is align-content: stretch.
This means that multiple lines of flex items will be distributed evenly along the cross axis.
To override this behavior, apply align-content: flex-start to the container.
When you're working in a single-line flex container (i.e., flex-wrap: nowrap), the properties to use to distribute space along the cross axis are align-items and align-self.
When you're working in a multi-line flex container (i.e., flex-wrap: wrap) – like in the question – the property to use to distribute flex lines (rows / columns) along the cross axis is align-content.
From the spec:
8.3. Cross-axis Alignment: the align-items and align-self properties
align-items sets the default alignment for all of the flex container’s items, including anonymous flex items. align-self allows this default alignment to be overridden for individual flex items.
8.4. Packing Flex Lines: the align-content
property
The align-content property aligns a flex container’s lines within the
flex container when there is extra space in the cross-axis, similar to
how justify-content aligns individual items within the main-axis.
Note, this property has no effect on a single-line flex container.
The align-content property takes six values:
flex-start
flex-end
center
space-between
space-around
stretch
Here's the definition for stretch:
stretch
Lines stretch to take up the remaining space. If the leftover free-space is negative, this value is identical to flex-start. Otherwise, the free-space is split equally between all of the lines, increasing their cross size.
In other words, align-content: stretch on the cross axis is similar to flex: 1 on the main axis.
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.
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
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.