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
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>
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).
What are the differences between flex-basis and width?
Consider flex-direction
The first thing that comes to mind when reading your question is that flex-basis
doesn't always apply to width
.
When flex-direction
is row
, flex-basis
controls width.
But when flex-direction
is column
, flex-basis
controls height.
Key Differences
Here are some important differences between flex-basis
and width
/ height
:
flex-basis
applies only to flex items. Flex containers (that aren't also flex items) will ignoreflex-basis
but can usewidth
andheight
.flex-basis
works only on the main axis. For example, if you're inflex-direction: column
, thewidth
property would be needed for sizing flex items horizontally.flex-basis
has no effect on absolutely-positioned flex items.width
andheight
properties would be necessary. Absolutely-positioned flex items do not participate in flex layout.By using the
flex
property, three properties –flex-grow
,flex-shrink
andflex-basis
– can be neatly combined into one declaration. Usingwidth
, the same rule would require multiple lines of code.
Browser Behavior
In terms of how they are rendered, there should be no difference between flex-basis
and width
, unless flex-basis
is auto
or content
.
From the spec:
7.2.3. The
flex-basis
propertyFor all values other than
auto
andcontent
,flex-basis
is resolved the same way aswidth
in horizontal writing modes.
But the impact of auto
or content
may be minimal or nothing at all. More from the spec:
auto
When specified on a flex item, the
auto
keyword retrieves the value
of the main size property as the usedflex-basis
. If that value is
itselfauto
, then the used value iscontent
.
content
Indicates automatic sizing, based on the flex item’s content.
Note: This value was not present in the initial release of Flexible
Box Layout, and thus some older implementations will not support it.
The equivalent effect can be achieved by usingauto
together with a
main size (width
orheight
) ofauto
.
So, according to the spec, flex-basis
and width
resolve identically, unless flex-basis
is auto
or content
. In such cases, flex-basis
may use content width (which, presumably, the width
property would use, as well).
The flex-shrink
factor
It's important to remember the initial settings of a flex container. Some of these settings include:
flex-direction: row
- flex items will align horizontallyjustify-content: flex-start
- flex items will stack at the start of the line on the main axisalign-items: stretch
- flex items will expand to cover the cross-size of the containerflex-wrap: nowrap
- flex items are forced to stay in a single lineflex-shrink: 1
- a flex item is allowed to shrink
Note the last setting.
Because flex items are allowed to shrink by default (which prevents them from overflowing the container), the specified flex-basis
/ width
/ height
may be overridden.
For example, flex-basis: 100px
or width: 100px
, coupled with flex-shrink: 1
, will not necessarily be 100px.
To render the specified width – and keep it fixed – you will need to disable shrinking:
div {
width: 100px;
flex-shrink: 0;
}
OR
div {
flex-basis: 100px;
flex-shrink: 0;
}
OR, as recommended by the spec:
flex: 0 0 100px; /* don't grow, don't shrink, stay fixed at 100px */
7.2. Components of
FlexibilityAuthors are encouraged to control flexibility using the
flex
shorthand
rather than with its longhand properties directly, as the shorthand
correctly resets any unspecified components to accommodate common
uses.
Browser Bugs
Some browsers have trouble sizing flex items in nested flex containers.
flex-basis
ignored in a nested flex container. width
works.
When using flex-basis
, the container ignores the sizing of its children, and the children overflow the container. But with the width
property, the container respects the sizing of its children and expands accordingly.
References:
- Chrome does not expand flex parent according to children's content
- Flex item overflowing when using flex-basis
- Difference between width and flex-basis
- Flex-basis is being ignored when sizing nested flex containers.
- flex-basis:100px does something different from width:100px+flex-basis:auto
Examples:
- https://jsfiddle.net/t419zhra/ (source: @Dremora)
- https://codepen.io/anon/pen/NVxaoy (source @Daniel)
- https://jsfiddle.net/voc9grx6/ (source: Chromium Bugs)
- https://jsfiddle.net/qjpat9zk/ (source: Chromium Bugs)
flex items using flex-basis
and white-space: nowrap
overflow inline-flex
container. width
works.
It seems that a flex container set to inline-flex
doesn't recognize flex-basis
on a child when rendering a sibling with white-space: nowrap
(although it could just be an item with undefined width). The container doesn't expand to accommodate the items.
But when the width
property is used instead of flex-basis
, the container respects the sizing of its children and expands accordingly. This is not a problem in IE11 and Edge.
References:
- inline flex container width not growing
- Inline flex container (display: inline-flex) is expanding the full width of parent container
Example:
- https://jsfiddle.net/p18h0jxt/1/ (from first post above)
flex-basis
(and flex-grow
) not working on table element
References:
- Why does flex-box work with a div, but not a table?
- Why doesn't flex-grow: 1 work for a table in Safari? (and Edge)
flex-basis
fails in Chrome and Firefox when the grandparent container is a shrink-to-fit element. The set-up works fine in Edge.
- Absolutely positioned container not expanding width to fit flexbox content
Like in the example presented in the link above, involving position: absolute
, the use of float
and inline-block
, will also render the same flawed output (jsfiddle demo).
Bugs affecting IE 10 and 11:
flex
shorthand declarations with unitlessflex-basis
values are ignoredflex-basis
doesn't account forbox-sizing: border-box
flex-basis
doesn't supportcalc()
- Importance is ignored on
flex-basis
when usingflex
shorthand
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
What does flex: 1 mean?
Here is the explanation:
https://www.w3.org/TR/css-flexbox-1/#flex-common
flex: <positive-number>
Equivalent to flex: <positive-number> 1 0. Makes the flex item flexible and sets the flex basis to zero, resulting in an item that
receives the specified proportion of the free space in the flex
container. If all items in the flex container use this pattern, their
sizes will be proportional to the specified flex factor.
Therefore flex:1
is equivalent to flex: 1 1 0
Related Topics
How to Change the Border/Outline Color for Input and Textarea Elements in Twitter Bootstrap
Consistent Font-Size Across Browsers (Web Development)
Alternative for Nth-Child for Older Ie Browsers
Footer Consisting of Two Right Triangles
Css: Borders Between Table Columns Only
CSS Flexbox Vertically/Horizontally Center Image Without Explicitely Defining Parent Height
Are There Any Practical Reasons to Use "Em" Instead of "Pt" Font Size Units
What Is the Benefit of Using Withstyles Over Makestyles
Css3 Animate: How to Have the Object Not Revert to Its Initial Position After Animation Has Run
What Is an Alternative to CSS Subgrid
How to Combine and Use Multiple Next.Js Plugins
Flexbox Max-Height Issue with Ie11
How to Change CSS Class for the Inputfield and Label When Validation Fails
How to Create a Triangular Shape with Curved Border