Flex items are shrinking below flex-basis
An initial setting of a flex container is flex-shrink: 1
.
This means that flex items are allowed to shrink in order to not overflow the container.
Once you disable this feature, flex items won't shrink below their specified flex-basis
values.
* { flex-shrink: 0; } /* NEW */
.item { background-color: red; border: 1px solid pink; margin: 10px; padding: 10px;}
.container { display: flex; border: 1px dashed black;}
.container .item { height: 50px; flex-grow: 1;}
.container3 { flex-direction: row; width: 500px;}
.container3 .a,.container3 .b { flex-basis: 200px;}
<div class="container container3"> <div class="item a">Segmentation fault</div> <div class="item b">Null pointer exception</div> <div class="item c">hello</div> <div class="item d">hello</div></div>
Why does width % on flex-container shrink past its contents, but flex-basis does not?
To understand this you need to consider two concepts: The "flex base size" and the "main size".
It's a bit tricky to explain but if you refer to the full algorithm in the specification You will notice that we first calculate the "flex base size" and later we calculate the "hypothetical main size" which is the final size.
For the "hypothetical main size":
The hypothetical main size is the item’s flex base size clamped according to its used min and max main sizes (and flooring the content box size at zero).
When you use width
you are affecting the "main sizes" that is used to define the final width (the "hypothetical main size"). In other words, it's not only the min-width
that is responsible of the non-shrinking effect but it's the "min max main sizes".
Here is another example to better understand:
.flex-container {
display: flex;
background-color: DodgerBlue;
}
.flex-container>div {
background-color: #f1f1f1;
margin: 2px;
padding: 2px;
font-size: 30px;
flex-basis: 20%;
width: 40px;
}
<div class="flex-container">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
Width of flex item is shrinking
I need to understand why does
width
not work whilemin-width
works...
Because flex items are set, by default, to flex-shrink: 1
, which means that they can shrink in order to minimize overflow of the container.
Your actual code (what the browser sees) is this:
#desktop_sidemenu {
width: 200px; /* author defined */
flex-shrink: 1; /* default setting */
}
You need to disable flex-shrink
:
#desktop_sidemenu {
width: 200px;
flex-shrink: 0;
}
Now, because the item can't shrink below 200px, it's equivalent to min-width: 200px
.
For more details, see "The flex-shrink
factor" in my answer here:
- What are the differences between flex-basis and width?
flex items not expanding or shrinking with flex-grow
There are two pieces missing in this puzzle.
You have the column widths set to
flex-grow: 1
. This means thatflex-basis
is still at its default value,auto
(content-based). So the column is sized based on the image size. You can addflex-basis: 0
, or just useflex: 1
.Full explanation: Make flex-grow expand items based on their original size
Flex items, by default, cannot shrink below the size of their content. Their initial setting is
min-width: auto
. You need to override this default withmin-width: 0
.Full explanation: Why don't flex items shrink past content size?
.row {
display: flex;
height: 100vh;
}
.column {
min-width: 0; /* new */
flex: 1; /* adjustment */
border: 2px solid #000;
transition: all 300ms ease-in-out;
}
.column:hover {
flex-grow: 4.3;
}
body {
margin: 0;
}
<div class="row">
<div class="column">
<img src="https://images.pexels.com/photos/2194261/pexels-photo-2194261.jpeg">
</div>
<div class="column">
<img src="https://images.pexels.com/photos/1276553/pexels-photo-1276553.jpeg">
</div>
<div class="column">
<img src="https://images.pexels.com/photos/774731/pexels-photo-774731.jpeg">
</div>
</div>
Why don't flex items shrink past content size?
The Automatic Minimum Size of Flex Items
You're encountering a flexbox default setting.
A flex item cannot be smaller than the size of its content along the main axis.
The defaults are...
min-width: auto
min-height: auto
...for flex items in row-direction and column-direction, respectively.
You can override these defaults by setting flex items to:
min-width: 0
min-height: 0
overflow: hidden
(or any other value, exceptvisible
)
Flexbox Specification
4.5. Automatic Minimum Size of Flex
ItemsTo provide a more reasonable default minimum size for flex items, this
specification introduces a newauto
value as the initial value of
themin-width
andmin-height
properties defined in CSS 2.1.
With regard to the auto
value...
On a flex item whose
overflow
isvisible
in the main axis, when specified on the flex item’s main-axis min-size property, specifies an automatic minimum size. It otherwise computes to0
.
In other words:
- The
min-width: auto
andmin-height: auto
defaults apply only whenoverflow
isvisible
. - If the
overflow
value is notvisible
, the value of the min-size property is0
. - Hence,
overflow: hidden
can be a substitute formin-width: 0
andmin-height: 0
.
and...
- The minimum sizing algorithm applies only on the main axis.
- For example, a flex item in a row-direction container does not get
min-height: auto
by default. - For a more detailed explanation see this post:
- min-width rendering differently in flex-direction: row and flex-direction: column
You've applied min-width: 0 and the item still doesn't shrink?
Nested Flex Containers
If you're dealing with flex items on multiple levels of the HTML structure, it may be necessary to override the default min-width: auto
/ min-height: auto
on items at higher levels.
Basically, a higher level flex item with min-width: auto
can prevent shrinking on items nested below with min-width: 0
.
Examples:
- Flex item is not shrinking smaller than its content
- Fitting child into parent
- white-space css property is creating issues with flex
Browser Rendering Notes
Chrome vs. Firefox / Edge
Since at least 2017, it appears that Chrome is either (1) reverting back to the
min-width: 0
/min-height: 0
defaults, or (2) automatically applying the0
defaults in certain situations based on a mystery algorithm. (This could be what they call an intervention.) As a result, many people are seeing their layout (especially desired scrollbars) work as expected in Chrome, but not in Firefox / Edge. This issue is covered in more detail here: flex-shrink discrepancy between Firefox and ChromeIE11
As noted in the spec, the
auto
value for themin-width
andmin-height
properties is "new". This means that some browsers may still render a0
value by default, because they implemented flex layout before the value was updated and because0
is the initial value formin-width
andmin-height
in CSS 2.1. One such browser is IE11. Other browsers have updated to the newerauto
value as defined in the flexbox spec.
Revised Demo
.container { display: flex;}
.col { min-height: 200px; padding: 30px; word-break: break-word}
.col1 { flex: 1; background: orange; font-size: 80px; min-width: 0; /* NEW */}
.col2 { flex: 3; background: yellow}
.col3 { flex: 4; background: skyblue}
.col4 { flex: 4; background: red}
<div class="container"> <div class="col col1">Lorem ipsum dolor</div> <div class="col col2">Lorem ipsum dolor</div> <div class="col col3">Lorem ipsum dolor</div> <div class="col col4">Lorem ipsum dolor</div></div>
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>
How to prevent a flex item from shrinking smaller than its content?
You should be able to achieve the Firefox behavior in Chrome by adding min-width: -webkit-min-content;
to .flexCol3
. This prevents it from shrinking below its min-content width. (This is what's supposed to happen by default, due to min-width:auto
introduced in the flexbox spec, but that hasn't been implemented in Chrome yet.)
As noted in comments below, IE doesn't seem to have a min-content
width keyword implemented, so you might have to do something hackier there (like min-width: 250px
). Fortunately, IE's next release (12?) does have min-width:auto
implemented, so this should Just Work like Firefox there, I'm told.
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.
Related Topics
How to Reduce Font Size Based on Length
Z-Index and Relative/Absolute Positioning
Jagged "Border" Showing Due to Background Colour on Wrapper Element with Border-Radius: 50%;
CSS Styles Not Being Loaded in Ie8
Position:Fixed in Windows Phone 7
What Is CSS "[Class*=My-Class] .My-Subclass" Doing
Retrieve or Set Less Variable from JavaScript
Why Do Non-Floating Parents of Floating Elements Collapse
How to Get Wkhtmltopdf to Display Th and Td Background Gradients
Import CSS Selector Styles in Another Selector? (Not @Import)
CSS: Selecting Table's <Td> Without The <Td>S of Nested Tables
Safari CSS Word-Break: Keep-All; Is Not Working
Multiple Divs with The Same Id Invalid
CSS Transform Scale to Background Image
Fill Parent Container and Reduce Image Resolution with Next/Image