Flex Items Are Shrinking Below Flex-Basis

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 while min-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.

  1. You have the column widths set to flex-grow: 1. This means that flex-basis is still at its default value, auto (content-based). So the column is sized based on the image size. You can add flex-basis: 0, or just use flex: 1.

    Full explanation: Make flex-grow expand items based on their original size




  1. 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 with min-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, except visible)

Flexbox Specification

4.5. Automatic Minimum Size of Flex
Items

To provide a more reasonable default minimum size for flex items, this
specification introduces a new auto value as the initial value of
the min-width and min-height properties defined in CSS 2.1.

With regard to the auto value...

On a flex item whose overflow is visible in the main axis, when specified on the flex item’s main-axis min-size property, specifies an automatic minimum size. It otherwise computes to 0.

In other words:

  • The min-width: auto and min-height: auto defaults apply only when overflow is visible.
  • If the overflow value is not visible, the value of the min-size property is 0.
  • Hence, overflow: hidden can be a substitute for min-width: 0 and min-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 the 0 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 Chrome

  • IE11

    As noted in the spec, the auto value for the min-width and min-height properties is "new". This means that some browsers may still render a 0 value by default, because they implemented flex layout before the value was updated and because 0 is the initial value for min-width and min-height in CSS 2.1. One such browser is IE11. Other browsers have updated to the newer auto 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



Leave a reply



Submit