3

A very short prehistory

My story begins with struggling to make overflow-wrap: break-word; working inside a flexbox. Flexbox container didn't want to understand that its item can be shrunk despite the fact that the item can break long words:

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex-column {
  display: flex;
}

.item {
  overflow-wrap: break-word;
  background-color: #fff;
  padding: 8px;
}
<div class="body">
  <div class="flex-column">
    <div class="item">
      This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get your example looking how you want it to look. But both values are being rendered correctly.
    </div>
  </div>
</div>

Fortunately, we can help flexbox to understand that it can shrink its item using min-width: 0; on the item:

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex-column {
  display: flex;
}

.item {
  overflow-wrap: break-word;
  background-color: #fff;
  padding: 8px;
  /* Okay, it fixes this */
  min-width: 0;
}
<div class="body">
  <div class="flex-column">
    <div class="item">
      This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get your example looking how you want it to look. But both values are being rendered correctly.
    </div>
  </div>
</div>

However, the real world is a little bit more complicated.

The problem

In our application, we have many nested flexboxes. So the example should look like this:

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex {
  display: flex;
}

.flex-column {
  display: flex;
}

.item {
  min-width: 0;
  padding: 8px;
  background-color: #fff;
  overflow-wrap: break-word;
}
<div class="body">
  <div class="flex">
    <div class="flex">
      <div class="flex">
        <div class="flex-column">
            <div class="item">
              This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get your example looking how you want it to look. But both values are being rendered correctly.
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

As you may see, the flex container of our flex-column ignores the fact that its children can shrink very well. I do not understand why is it behaves that way. Could you explain this to me? Why is the flexbox-container doesn't respect its child flexbox min-width: 0?

The solution that I've found is to set min-width: 0 to all flexboxes in the hierarchy which looks very hacky and dangerous because I can break our application layout in unexpected places.

TylerH
  • 20,799
  • 66
  • 75
  • 101
algebraic
  • 306
  • 2
  • 10

1 Answers1

2

To understand this, simply add border with different colors to your items and you will see that you have overflow at different levels. More precesily, we have only one overflow that is moving to a lower lever after adding each min-width.

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex {
  display: flex;
}

.flex-column {
  display: flex;
}

.item {
  padding: 8px;
  background-color: #fff;
  overflow-wrap: break-word;
}
<div class="body">
  <div class="flex" style="border:5px solid red;">
    <div class="flex" style="border:5px solid green;">
      <div class="flex" style="border:5px solid blue;">
        <div class="flex-column" style="border:5px solid yellow;">
          <div class="item" style="border:5px solid pink;">
            This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get
            your example looking how you want it to look. But both values are being rendered correctly.
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Every min-width will fix one overflow, allow the element to shrink and move the overflow to next level. That's why you need a cascading min-width.

Adding one:

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex {
  display: flex;
}

.flex-column {
  display: flex;
}

.item {
  padding: 8px;
  background-color: #fff;
  overflow-wrap: break-word;
}
<div class="body">
  <div class="flex" style="border:5px solid red;">
   <!-- adding min-width at this level -->
    <div class="flex" style="border:5px solid green;min-width:0;">
      <div class="flex" style="border:5px solid blue;">
        <div class="flex-column" style="border:5px solid yellow;">
            <div class="item" style="border:5px solid pink;">
              This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get your example looking how you want it to look. But both values are being rendered correctly.
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Adding another:

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex {
  display: flex;
}

.flex-column {
  display: flex;
}

.item {
  padding: 8px;
  background-color: #fff;
  overflow-wrap: break-word;
}
<div class="body">
  <div class="flex" style="border:5px solid red;">
    <div class="flex" style="border:5px solid green;min-width:0;">
      <!-- adding min-width at this level -->
      <div class="flex" style="border:5px solid blue;min-width:0">
        <div class="flex-column" style="border:5px solid yellow;">
          <div class="item" style="border:5px solid pink;">
            This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get
            your example looking how you want it to look. But both values are being rendered correctly.
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

Again:

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex {
  display: flex;
}

.flex-column {
  display: flex;
}

.item {
  padding: 8px;
  background-color: #fff;
  overflow-wrap: break-word;
}
<div class="body">
  <div class="flex" style="border:5px solid red;">
    <div class="flex" style="border:5px solid green;min-width:0;">
      <div class="flex" style="border:5px solid blue;min-width:0">
       <!-- adding min-width at this level -->
        <div class="flex-column" style="border:5px solid yellow;min-width:0;">
          <div class="item" style="border:5px solid pink;">
            This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get
            your example looking how you want it to look. But both values are being rendered correctly.
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

The last one:

.body {
  width: 300px;
  border: 1px solid black;
  padding: 8px;
  background-color: #ccc;
}

.flex {
  display: flex;
}

.flex-column {
  display: flex;
}

.item {
  padding: 8px;
  background-color: #fff;
  overflow-wrap: break-word;
}
<div class="body">
  <div class="flex" style="border:5px solid red;">
    <div class="flex" style="border:5px solid green;min-width:0;">
      <div class="flex" style="border:5px solid blue;min-width:0">
        <div class="flex-column" style="border:5px solid yellow;min-width:0;">
         <!-- adding min-width at this level -->
          <div class="item" style="border:5px solid pink;min-width:0">
            This is Spaaaaaaaaaaaaaaaarrrrrrrrrrrrrttttttttttttttaaaaaaaaaaaaaaaaaaaaaa!11 It's not a bug. Firefox is correctly implementing min-width: auto for flex items. When you change it to min-width: 0, you're just using a different value for min-width to get
            your example looking how you want it to look. But both values are being rendered correctly.
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • Thanks for your answer! Your examples with borders are great and allow to understand the problem better. However, I still don't understand why grandparent flex "knows" that there is some overflowing grandchild? Why is it not sufficient to set `min-width: 0` at the deepest level? I thought that flexbox container should "ask" its child: — How far can you shrink? — I can shrink very well because my child has `min-width: 0`. — Okay, I guess I can shrink too because you're my only children and you can shrink to 0. So I thought it should work from bottom to top recursively. – algebraic Nov 08 '18 at 14:38
  • 1
    @algebraic because at the deepest level there is no overflow. If you check well the borders, you will see that the green is overflowing the red (this is our overflow) then after we no more have overflow (adding min-width there will do nothing). So we don't have *many* overflows but only one at different level. If you fix the first one, you will shrink one border and move the overflow to a lower level and so on. So the first one will not shrink and after that everything is ok. If the first one shrink we move the problem to its child – Temani Afif Nov 08 '18 at 14:44
  • 1
    I would really recommend you add additional CSS in the CSS side of the snippet; it took me several glances to figure out how your snippets differed from each other because the CSS section was the same in all of them. – TylerH Nov 08 '18 at 15:15
  • You can also solve this with one declaration: `.flex > div { min-width: 0; }` cc @algebraic but be cautious of how it will affect additional descendant divs, should you add them. – TylerH Nov 08 '18 at 15:17
  • @TylerH he's aware about the solution but we want to understand why it's needed at each level. That's why I tried to add them one by one to show this ... I will probably add some HTML comments to make it clear when I add min-width – Temani Afif Nov 08 '18 at 15:20
  • @TemaniAfif OP's comment in the question seemed to indicate to me that he thought he'd have to set it multiple times, hence my suggestion (e.g. "all flexboxes in the hierarchy which looks hacky")... could just be how I parse that language vs how you do :-) – TylerH Nov 08 '18 at 15:23
  • @TylerH > "that he thought he'd have to set it multiple times". Oh, sorry, of course, I didn't think so :) In the real application all those flexes have different classes and define the structure of the application. Even if I set `min-width: 0` to all of them it can be broken at any moment if someone adds a new hierarchy level without `min-width: 0` (which is highly likely). – algebraic Nov 08 '18 at 15:30
  • @TemaniAfif It's quite interesting insight that the overflow happens at the highest level! I didn't notice that. But it is still unclear to me _why_ does it happen. Shouldn't the green flex "ask" the min-size of the red one? And the red one seems to have no reasons to be so wide (because it's children is not wide too and so on). Sorry for my misunderstanding :) I think I miss something important in the whole flexbox ideology, I even tried to read the specification in order to find an answer. – algebraic Nov 08 '18 at 15:39
  • @algebraic Well, "it can be broken at any moment if someone adds a new hierarchy level" actually setting that property to `.flex > div` will cause it to work *regardless* of how many hierarchy levels there are, so long as you keep the same pattern of nested `
    ` (which I think is a reasonable assumption based on the repetition in your real-world example in the question) :-)
    – TylerH Nov 08 '18 at 15:42
  • 1
    @algebraic start your thinking from the bottom. The last element has the content and there is no width restriction (it's fine). Its parent also has not width restriction (still fine and no overflow) .. until we reach the element with the width restriction and here we have overflow because we didn't allow the shrink ... so allowing the shrink where the overlow happen with fix the issue and you can start you logic again from the bottom and you will reach the overflow point that we fix again and so on. – Temani Afif Nov 08 '18 at 15:49
  • @algebraic you will tell me that CSS is from to to bottom and I will tell you yes when it comes to styling as we don't go upwards but the content define the behavior of container so we see the content and move up to define our behavior – Temani Afif Nov 08 '18 at 15:50
  • @TylerH I cannot simply set `.flex > div` because: 1. All the flexes have different classes, there is no single `.flex`. 2. It's React application with styled components, there is no such thing as global CSS (almost), actually. My example is a toy one, in the real application it's different React components nested inside each other. – algebraic Nov 08 '18 at 15:52
  • @algebraic it's not specific to flexbox by the way, it may happen even with simple styles. Here is another example : https://jsfiddle.net/cptq3zk7/ .. you can fix the overflow by adding a cascading width:100% same as your example – Temani Afif Nov 08 '18 at 15:52
  • @algebraic here is the first width:100% https://jsfiddle.net/cptq3zk7/1/ and so on ... exactly the same logic – Temani Afif Nov 08 '18 at 15:56
  • @algebraic I don't follow your reasoning on step 1; it seems to indicate a misunderstanding of how CSS classes work. You can decide what styles get applied to each selector and what elements you want to apply the styles to by adjusting that selector. There's no reason you can't have `.flex > div { /* one style here */ }` followed by `.specific-flex-element-selector { /* element-specific styles here */ }`. As for step 2, that it's React-based is irrelevant to the fact you should be able to see/provide accurate generated markup. Your real world example should be more realistic, in that case. – TylerH Nov 08 '18 at 16:01
  • 1
    @TylerH, I think I should replace the repetition of `class="flex"` to something more meaningful, thanks! The question is more about "why is it works in that way?". Of course, I'm interested in how I can possibly fix it, but it's really specific to my application hierarchy and it's quite hard to reproduce it here. To simplify the problem: imagine that the code snippet starting from `
    ` is extracted into react component and can be inserted into any container (including the fixed-width block `div` with flex inside). How can I guarantee that it won't overflow the parent?
    – algebraic Nov 08 '18 at 16:18
  • @TemaniAfif Thank you! It seems to be clearer for me now. Your fiddle about the same inline-block behavior has shed more light on the behavior of the content-width dependent elements. If I'll remove `display: inline-block` and set `overflow-wrap: break-words` it will work because block elements seem to be depending on the parent width only. – algebraic Nov 08 '18 at 16:27