1

Consider the following HTML (JSFiddle):

<div style="display: flex; align-items: flex-start; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan; flex-shrink: 0">
    Yo
  </p>
  <div style="display: flex; flex: 1 1 0px; align-items: flex-start; background-color: green; overflow: visible">
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
  </div>
</div>

<!-- magenta 500px indicator here added for comparison of width -->
<div style="width: 500px; height: 5px; background-color: magenta; margin-bottom: 10px"></div>

We have a 500px wide flex container, and inside that container are two children: one which takes up 50px with flex-shrink: 0 and one which is supposed to take up the remaining space (in this case, 450px) using flex: 1 1 0px. However, it's actually expanding slightly further out, and its actual rendered width is 472.83px. If you add more content inside it, it gets bigger and bigger, unless you set a width: 0px on it, in which case its rendered width is 450px as expected.

  1. Why is this container's width expanding to be larger than 450px? I thought that using flex-basis: 0px meant that the effective size for flex purposes would always be 0px, and as such I would expect the container to grow to fill its parent's available width regardless of its content.
  2. With 13 of the <p> tags inside the container, it still has the 450px expected with, even though the text overflows (as expected). However, with the 14th <p> tag, the container starts growing, and after the 14th it keeps growing, by about 33.77px per <p> tag. What limit is getting hit on the 14th tag that causes the container to behave this way?
  3. Why does setting a width: 0px on this container cause it to have 450px width as I expected? I thought flex-basis always took priority over width.

I originally thought this might be a browser rendering issue, but the issue happens the same way in Chrome, Firefox, and Safari.

Noah Gilmore
  • 1,319
  • 2
  • 15
  • 24

2 Answers2

1

The second flex item (green) overflows the primary container for two reasons:

  1. That's the minimum width it can achieve with the content it has before flexibility properties are applied. In other words, the browser establishes the main size of the container before factoring in flex-shrink: 0.

    enter image description here

  2. The default minimum width of flex items is min-width: auto (meaning the item can't be smaller than the size of its content), and the nested flex container is also a flex item. When you override the default—with min-width: 0, for example—it fits.

    enter image description here

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
0

First of all, you can remove the flex property and you will still get the same result. I am also removing the properties that won't affect the result as well:

<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan; flex-shrink: 0">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
    <p style="flex-shrink: 0">This is some text</p>
  </div>
</div>

<!-- magenta 500px indicator here added for comparison of width -->
<div style="width: 500px; height: 5px; background-color: magenta; margin-bottom: 10px"></div>

Now, to understand what is happening, we need to add the properties one by one.

The initial configuration is that all the elements need to shrink so we get the following:

<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>

<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>

<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>

<!-- magenta 500px indicator here added for comparison of width -->
<div style="width: 500px; height: 5px; background-color: magenta; margin-bottom: 10px"></div>

You can clearly notice that in all the 3 cases, the text will wrap in order to try to get inside the 500px. Notice the difference in the wrapping between the first and second case.

The second case is the maximum wrapping you can have with your text since by default we cannot split words. And if you start adding more text, you will have overflow like in the 3rd case because there is no room for wrapping and for shrinking.

Now let's introduce flex-shrink: 0 for each case

The first one:

.shrink p {
  flex-shrink:0;
}
<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>
<br>
<div  style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;flex-shrink:0">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>
<br>
<div class="shrink" style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>

<!-- magenta 500px indicator here added for comparison of width -->
<div style="width: 500px; height: 5px; background-color: magenta; margin-bottom: 10px"></div>

By adding flex-shrink: 0 to the blue element we still have some space for the green element so it won't overflow and by adding flex-shrink: 0 to its content it will overflow but the green element won't get bigger (due to the shrink effect as well)

Now the second case which is the case of your question:

.shrink p {
  flex-shrink:0;
}
<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>
<br>

<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;flex-shrink:0;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>
<br>

<div class="shrink" style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;flex-shrink:0;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>

<!-- magenta 500px indicator here added for comparison of width -->
<div style="width: 500px; height: 5px; background-color: magenta; margin-bottom: 10px"></div>

Notice how adding flex-shrink: 0 to the blue element already create an overflow of the green one because this one cannot be smaller than its min content (when all the text is already wrapped). This is the default behavior of flex items. Adding flex-shrink:0 to its content won't change nothing because a flex item will always try to shrink but not beyond its min-width.

That's why adding min-width:0 (or width:0) can fix this issue:

.shrink p {
  flex-shrink:0;
}
<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>
<br>

<div style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;flex-shrink:0;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>
<br>

<div class="shrink" style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;flex-shrink:0;">
    Yo
  </p>
  <div style="display: flex; background-color: green; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>

<br>

<div class="shrink" style="display: flex; background-color: blue; width: 500px;">
  <p style="width: 50px; background-color: cyan;flex-shrink:0;">
    Yo
  </p>
  <div style="display: flex; background-color: green;min-width:0; ">
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
    <p >This is some text</p>
  </div>
</div>

<!-- magenta 500px indicator here added for comparison of width -->
<div style="width: 500px; height: 5px; background-color: magenta; margin-bottom: 10px"></div>

Now, I let you imagine the 3rd case where we add more content. We simply increase the min-width constraint a little each time and we make the green element bigger.

Related: Why don't flex items shrink past content size?

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Nice, this makes a lot of sense. The thing I wasn't understanding is that flex items aren't allowed to shrink past their min width (which by default is auto, aka the min-width of their content) by default, unless you set a different min-width. Thanks for clarifying that! – Noah Gilmore Jul 18 '22 at 15:22