In what circumstances is flex-shrink applied to flex elements and how does it work?
In order to see flex-shrink
in action, you need to be able to make its container smaller.
HTML:
<div class="container">
<div class="child one">
Child One
</div>
<div class="child two">
Child Two
</div>
</div>
CSS:
div {
border: 1px solid;
}
.container {
display: flex;
}
.child.one {
flex: 1 1 10em;
color: green;
}
.child.two {
flex: 2 2 10em;
color: purple;
}
- http://jsfiddle.net/GyXxT/ (unprefixed -- Opera or Firefox nightly build)
- http://jsfiddle.net/GyXxT/1/ (webkit)
In this example, both child elements ideally want to be 10em wide. If the parent element is greater than 20em wide, the 2nd child will take twice as much leftover space as the 1st child, making it appear bigger. If the parent element is less than 20em wide, then the 2nd child will have twice as much shaved off of it as the 1st child, making it look smaller.
Current flexbox support: Opera (unprefixed), Chrome (prefixed), IE10 (prefixed, but uses slightly different property names/values). Firefox currently uses the old spec from 2009 (prefixed), but the new spec is supposed to be available in experimental builds right now (unprefixed).
Understanding flex-grow and flex-shrink when using flex-basis
Percentage lengths are relative to their containing blocks.
Therefore, if the flex container has a width of 200px, and the flex items are set to flex-basis: 50%
, then each item will resolve to 100px.
Of course, in flex layout, flex-basis
represents the initial main size or, the size before flex-grow
and flex-shrink
are applied.
You have flex-grow
disabled, so nothing happens there.
But you have flex-shrink
enabled, so the items will shrink below 100px when necessary to prevent an overflow of the container.
In this case, because all items are set to flex-shrink: 1
, they will shrink in equal proportion.
article { display: flex; width: 200px; border: 1px solid black;}
[one] > section { flex: 0 1 50px;}
[two] > section { flex: 0 1 50%;}
[three] > section { flex: 0 0 50%;}
/* non-essential demo styles */section { height: 50px; background-color: lightgreen; border: 1px solid #ccc; display: flex; justify-content: center; align-items: center; box-sizing: border-box;}
<p>container width 200px in all cases</p><article one> <section><span>50px</span></section> <section><span>50px</span></section> <section><span>50px</span></section> <section><span>50px</span></section></article>
<hr>
<p><code>flex-shrink</code> enabled</p>
<article two> <section><span>50%</span></section> <section><span>50%</span></section> <section><span>50%</span></section> <section><span>50%</span></section></article>
<hr>
<p><code>flex-shrink</code> disabled</p>
<article three> <section><span>50%</span></section> <section><span>50%</span></section> <section><span>50%</span></section> <section><span>50%</span></section></article>
When does flex-grow switch to flex-shrink, and vice-versa?
The flex-basis
property sets the initial main size of the flex item, before free space is distributed by other flex properties.
This means that the flex item is first sized for its defined width (in flex-direction: row
) or height (in flex-direction: column
).
In other words, in a row-direction flex container, where flex-basis: 100px
is equivalent to width: 100px
, the item would have a width of 100px.
That's the initial main size.
THEN, other flex properties are applied. Namely, flex-grow
and flex-shrink
.
If the container is wider than 100px, then flex-grow: 1
expands the item to fill the extra space.
If the container is less than 100px, then flex-grow: 1
is ignored and flex-shrink: 1
reduces the size of the item according to a flex sizing algorithm.
So, to answer your question:
When does flex switch from grow to shrink?
Assuming both properties are enabled (i.e., they are not flex-grow: 0
or flex-shrink: 0
), the switch will occur when the sum total of flex-basis
/ width
/ height
on flex items is no longer less or more than the length of the container.
To make this a bit more clear:
When the sum total of
flex-basis
/width
/height
on flex items is no longer more than the length of the container, this means there is no overflow and the items don't consume all available space.flex-grow
works.When the sum total of
flex-basis
/width
/height
on flex items is no longer less than the length of the container, this means there is overflow and the items must shrink to fit inside the container.flex-shrink
works.
From the spec:
flex-grow
This [property] specifies the flex grow factor, which determines how much the flex item will grow relative to the rest of
the flex items in the flex container when positive free space is
distributed.
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.
flex-basis
This [property] specifies the initial main size of the flex item,
before free space is distributed according to the flex factors.https://www.w3.org/TR/css-flexbox-1/#flex-property
How does flex-shrink factor in padding and border-box?
Flexbox defines this as
For every unfrozen item on the line, multiply its flex shrink factor
by its inner flex base size, and note this as its scaled flex
shrink factor. Find the ratio of the item’s scaled flex shrink
factor to the sum of the scaled flex shrink factors of all
unfrozen items on the line. Set the item’s target main size to
its flex base size minus a fraction of the absolute value of the
remaining free space proportional to the ratio.
Simplifying, frozen flex items are those which can't or don't have to be flexed anymore. I will assume no min-width
restrictions and non-zero flex shrink factors. This way all flex items are initially unfrozen and they all become frozen after only one iteration of the flex loop.
The inner flex base size depends on the value of box-sizing
, defined by CSS2UI as
content-box
: The specified width and height (and respective min/max properties) apply to the width and height respectively of the
content box of the element. The padding and border of the element are
laid out and drawn outside the specified width and height.
border-box
: Length and percentages values for width and height (and respective min/max properties) on this element determine the
border box of the element. That is, any padding or border specified on
the element is laid out and drawn inside this specifiedwidth
andheight
. The content width and height are calculated by
subtracting the border and padding widths of the respective sides from
the specified width and height properties. [...] Used values, as
exposed for instance through getComputedStyle(), also refer to the
border box.
Basically, that means that sizes (widths, flex bases) have an inner an an outer variant. The inner size includes only the content, the outer one also includes paddings and border widths. The length specified in the stylesheet will be used as the inner size in case of box-sizing: content-box
, or as the outer one in case of box-sizing: border-box
. The other can be calculated by adding or subtracting border and padding widths.
Neglecting lots of details, the algorithm would be something like
let sumScaledShrinkFactors = 0,
remainingFreeSpace = flexContainer.innerMainSize;
for (let item of flexItems) {
remainingFreeSpace -= item.outerFlexBasis;
item.scaledShrinkFactor = item.innerFlexBasis * item.flexShrinkFactor;
sumScaledShrinkFactors += item.scaledShrinkFactor;
}
for (let item of flexItems) {
let ratio = item.scaledShrinkFactor / sumScaledShrinkFactors;
item.innerWidth = item.innerFlexBasis + ratio * remainingFreeSpace;
}
With no paddings, it's as you explain
(width)
innerW │ padd │ outerW
───────┼──────┼───────
300px * (1 + 2 / 1000px * -200px) = 180px │ 0px │ 180px
200px * (1 + 1 / 1000px * -200px) = 160px │ 0px │ 160px
100px * (1 + 2 / 1000px * -200px) = 60px │ 0px │ 60px
───────┼──────┼───────
400px │ 0px │ 400px
With 10px
horizontal paddings, the available space is reduced by 3 * 2 * 10px = 60px
, so now it's -260px
.
(width)
innerW │ padd │ outerW
───────┼──────┼───────
300px * (1 + 2 / 1000px * -260px) = 144px │ 20px │ 164px
200px * (1 + 1 / 1000px * -260px) = 148px │ 20px │ 168px
100px * (1 + 2 / 1000px * -260px) = 48px │ 20px │ 68px
───────┼──────┼───────
340px │ 60px │ 400px
When you use box-sizing: border-box
, the specified flex bases are the outer ones, so the paddings are subtracted from them to calculate the inner ones, which are 280px
, 180px
, 80px
. Then the sum of the scaled flex shrink factors becomes 2*280px + 180px + 2*80px = 900px
. The available space is like in the case with no padding, because the outer flex bases are the same. Note the width
retrieved by getComputedStyle
will now be the outer one, so paddings are added back at the end.
(width)
innerW │ padd │ outerW
────────┼──────┼────────
280px * (1 + 2 / 900px * -200px) ≈ 155.6px │ 20px │ 175.6px
180px * (1 + 1 / 900px * -200px) = 140.0px │ 20px │ 160.0px
80px * (1 + 2 / 900px * -200px) ≈ 44.4px │ 20px │ 64.4px
────────┼──────┼────────
340.0px │ 60px │ 400.0px
How to calculate flex shrink when flex items have a different flex basis
Neglecting lots of details, the algorithm is something like this
let sumScaledShrinkFactors = 0,
remainingFreeSpace = flexContainer.innerMainSize;
for (let item of flexItems) {
remainingFreeSpace -= item.outerFlexBasis;
item.scaledShrinkFactor = item.innerFlexBasis * item.flexShrinkFactor;
sumScaledShrinkFactors += item.scaledShrinkFactor;
}
for (let item of flexItems) {
let ratio = item.scaledShrinkFactor / sumScaledShrinkFactors;
item.innerWidth = item.innerFlexBasis + ratio * remainingFreeSpace;
}
So the formula is like
flexBasis * (1 + shrinkFactor / sumScaledShrinkFactors * remainingFreeSpace)
First case
1*600px + 1*200px ─┐ width
│ ───────
600px * (1 + 1 / 800px * -200px) = 450px
200px * (1 + 1 / 800px * -200px) = 150px
│ ───────
600px - (600px + 200px) ────┘ 600px
Second case
2*600px + 1*200px ──┐ width
│ ───────
600px * (1 + 2 / 1400px * -200px) ≈ 429px
200px * (1 + 1 / 1400px * -200px) ≈ 171px
│ ───────
600px - (600px + 200px) ─────┘ 600px
Very confused about Flex Shrink and Grow
.container { display: flex; height: 200px; border: 1px dashed black;}
.box1 { flex: 1 0 400px; /* flex-grow, flex-shrink, flex-basis */ max-width: 25%; background-color: green;}
.box2 { flex-grow: 1; background-color: orangered;}
<div class="container"> <div class="box1"></div> <div class="box2"></div></div>
How can I make flex items shrink until wrapping is necessary, and then expand to fill the new space?
Here's a snippet that I believe achieves what you want:
<div>
<div class="container">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
<div class="box">4</div>
<div class="box">5</div>
</div>
</div>
<style>
.container {
display: flex;
max-width: 1200px;
flex-wrap: wrap;
}
.box {
outline: solid red 1px;
min-width: 400px;
flex-grow: 2;
/* flex: 2 0 50%; */ /* If you want to ensure that you never get more than 2 items per row */
}
</style>
In sum, the property you want is flex-grow
: it ensures that the component will fill up available space on its row up to the specified factor (in this case 2, to go from 400px width up to 800px).
If it's important not to fit more than two elements on a row on wider screens, I added an alternative flex
property for that which uses a flex-basis
of 50% to make the components "greedier" in flowing onto a row than they would be otherwise.
How are heights of flex items calculated when flex-direction: column is applied?
You are running it a complex case where another specification need to be considered and also the min-height
constraint.
When using flex:1
the browser is setting flex-basis: 0%
. Pay attention to the %
because all the trick is there. You have a percentage so you need a reference to resolve it and we are dealing with a height auto so the reference doesn't exist. At the end it's like you didn't set any flex-basis
that's why you see no difference.
Now if you use flex-basis: 0px
you will have a different behavior
.container {
display: flex;
flex-direction: column;
border: 1px solid;
}
.item {
flex: 1 1 0px;
border: 1px solid red;
min-height: 0;
}
<div class="container">
<div class="item">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quaerat similique nihil, mollitia voluptates iste eos atque unde repellendus iure voluptatibus nulla explicabo laboriosam, harum, eum dicta. Maiores quia aliquid in.</div>
<div class="item">
Lorem
</div>
</div>
Related Topics
Does IE9 Have a File Size Limit for CSS
How to Fade In/Out Multiple Texts Using CSS/Jquery Like on Droplr
What Does !Default in a CSS Property Value Mean
Overflow: Overlay Doesn't Work in Firefox
Sass/Compass - Convert Hex, Rgb, or Named Color to Rgba
Google Chrome > Textboxes > Yellow Border When Active..
Stop Firefox Dpi Scaling (When Windows Setting Is at 125%)
Accessing an Array Key in SASS
How to Center a Div with Bootstrap2
Sass and Bootstrap - Mixins VS. @Extend
Webkit Animation Is Leaving Junk Pixels Behind on the Screen
Angular2 - Adding [_Ngcontent-Mav-X] to Styles