Why Don't Flex Items Shrink Past Content Size

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>

Flex item is not shrinking smaller than its content

Sometimes you need to look at all flex items (up and down the HTML structure), and check to see if they need the overflow / min-width override.

In this case, there are flex items at higher levels that still default to min-width: auto, preventing the reduction in size.

.mdc-list-item {  flex-shrink: 1;  min-width: 0;  overflow: hidden;  text-overflow: ellipsis;}.mdc-list {  display: flex;  flex-direction: column;}
/* RULES ADDED */.mdc-list { min-width: 0;}.mdc-list-item__text { overflow: hidden;}.mdc-list-item__text__secondary { text-overflow: ellipsis; overflow: hidden;}
<script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.js"></script><link href="https://unpkg.com/material-components-web@latest/dist/material-components-web.css" rel="stylesheet" /><div style="width: 100%; max-width: 800px; margin: 0 auto; display: flex;">  <ul class="mdc-list mdc-list--two-line mdc-elevation--z1" style="flex: 1;">    <li class="mdc-list-item" title="Test Item 1 Description" channel-id="1">      <img class="mdc-list-item__start-detail grey-bg" style="width: 40px; height: 40px;" src="https://material-components-web.appspot.com/images/animal3.svg" alt="Brown Bear">
<span class="mdc-list-item__text"> Test Item 1 <span class="mdc-list-item__text__secondary">Test Item 1 Description</span> </span>
<div class="mdc-list-item__end-detail"> <i class="mdc-icon-toggle material-icons color-primary-text-inv toggle-notifications-email" style="margin-top: -12px;" role="button"> X </i> </div> <div class="mdc-list-item__end-detail" style="margin-left: 64px;"> <i class="mdc-icon-toggle material-icons color-primary-text-inv toggle-notifications-notification" style="margin-top: -12px; margin-left: -24px;" role="button"> Y </i> </div> </li> <li role="separator" class="mdc-list-divider"></li> <li class="mdc-list-item" title="Here you can read the long description of Item 2 which refuses to shrink" channel-id="2"> <img class="mdc-list-item__start-detail grey-bg" style="width: 40px; height: 40px;" src="https://material-components-web.appspot.com/images/animal3.svg" alt="Brown Bear">
<span class="mdc-list-item__text"> Test Item 2 <span class="mdc-list-item__text__secondary">Here you can read the long description of Item 2 which refuses to shrink</span> </span>
<div class="mdc-list-item__end-detail"> <i class="mdc-icon-toggle material-icons color-primary-text-inv toggle-notifications-email" style="margin-top: -12px;" role="button"> X </i> </div> <div class="mdc-list-item__end-detail" style="margin-left: 64px;"> <i class="mdc-icon-toggle material-icons color-primary-text-inv toggle-notifications-notification" style="margin-top: -12px; margin-left: -24px;" role="button"> Y </i> </div> </li> </ul></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>

Flex items not shrinking when window gets smaller

An initial setting on flex items is min-width: auto. This means that a flex item cannot, by default, be smaller than its content.

You can override this setting with min-width: 0 or overflow with any value other than visible. Add this to your code:

.plot-col {
min-width: 0;
}

Revised Fiddle

More info: Why doesn't flex item shrink past content size?

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>

Flex parent won't shrink past child content, even though child has `0` computed width

From all the answers so far, I understand there's no clean CSS way to reduce the size of the parent based on computed widths of all children, while the children are being animated, using flexbox.

Technically, this means flexbox props (-shrink, -basis, -grow) can't be used to animate here and (max-)width should be used instead. I must admit, I still find it hard to believe. I would have thought flexbox got this covered.

The problem with animating max-width or width is you can't animate from 0 to auto without JavaScript.

Which leads to the following solution:

[...document.querySelectorAll('.flexer')].forEach(el => {
const item = el.querySelector('.extra');
if (item) {
el.onmouseenter = () => {
item.style.maxWidth = item.scrollWidth + 'px'
}
el.onmouseleave = () => {
item.style.maxWidth = '0'
}
}
})
.flexer {
display: inline-flex;
border: 1px solid red;
}
.extra {
transition: max-width .42s cubic-bezier(.4, 0, .2, 1);
overflow: hidden;
max-width: 0;
white-space: nowrap;
}
<div class="flexer">
<div>
test
</div>
<div class="extra">
 extra
</div>
</div>

<div class="flexer">
<div>
test
</div>
<div class="extra">
 I have a lot more content...
</div>
</div>


Related Topics



Leave a reply



Submit