chris
chris

Reputation: 1811

How to make flexbox items shrink correctly when in a nested container?

If I set up a nested flexbox container like so:

<div class="container1">
  <div class="grow1">
    <div class="container2">
      <div class="grow2"></div>
    </div>
  </div>
</div>

...and then set the width of grow2 such that it is wider than container1 then grow2 overflows container1.

I believe this should not happen since flex elements are supposed to shrink when they are larger than the flex container.

If I set the flex-basis of grow2 then this works as expected.

Please see the following example for a demo:

https://jsfiddle.net/chris00/ot1gjjtk/20/

Please use Chrome or Firefox for this

Furthermore, I read that the flexbox spec says that width and flex-basis should have the same effect (when using horizontal layouts) which they clearly don't.

Now I could just use flex-basis instead of width, but... Edge does the same thing for both flex-basis and width, and it does it in the "wrong" way. IE11 does it wrong also (although that appears to have multiple flexbox bugs). Please check out the demo with Edge.

So how is this supposed to work?

Are there bugs in all browsers?

Is flex-basis actually supposed to be different from width (in simple horizontal layouts)?

Or is Edge correct and both width and flex-basis are supposed to overflow the parent container?

Finally, is there a workaround that can fix the overflow for Edge (and even IE11)?

.container1 {
  margin-top: 10px;
  display: flex;
  width: 200px;
  height: 50px;
  background-color: red;
}

.grow1 {
  flex-grow: 1;
  height: 40px;
  background-color: green;
}

.container2 {
  display: flex;
  height: 30px;
  background-color: yellow;
}

.grow2a {
  flex-grow: 1;
  flex-basis: 400px;
  height: 20px;
  background-color: turquoise;
}

.grow2b {
  flex-grow: 1;
  width: 400px;
  height: 20px;
  background-color: turquoise;
}
<div class="container1">
  <div class="grow1">
    <div class="container2">
      <div class="grow2a">Working (flex-basis)</div>
    </div>
  </div>
</div>

<div class="container1">
  <div class="grow1">
    <div class="container2">
      <div class="grow2b">Not working (width)</div>
    </div>
  </div>
</div>

Upvotes: 10

Views: 5309

Answers (1)

Michael Benjamin
Michael Benjamin

Reputation: 372234

The Problem

As far as the spec is concerned, this isn't an issue pertaining to flex-basis, width, flex-grow or flex. It's something entirely different.

4.5. Implied 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.

In other words, a flex item, by default, cannot be smaller than the length of its content (essentially, the longest word or fixed-size element).

The item cannot stay within its container (or even render a scroll bar or ellipsis), because its content is not permitted to overflow. The content simply expands the item. This behavior applies to fixed-sizing, as well (such as the flex-basis: 400px in your code).

Again, the initial settings are:

  • min-width: auto, in row-direction
  • min-height: auto, in column-direction

For a more complete explanation see this post:


Solution for Chrome, Safari, Firefox and Edge

The standard solution to this problem is simple: override the default.

In your code, add min-width: 0 to .grow1.

That solves the problem in Chrome, Safari, FF and Edge.

.container1 {
  margin-top: 10px;
  display: flex;
  width: 200px;
  height: 50px;
  background-color: red;
}

.grow1 {
  flex-grow: 1;
  height: 40px;
  background-color: green;
  min-width: 0; /* NEW */
}

.container2 {
  display: flex;
  height: 30px;
  background-color: yellow;
}

.grow2a {
  flex-grow: 1;
  flex-basis: 400px;
  height: 20px;
  background-color: turquoise;
}

.grow2b {
  flex-grow: 1;
  width: 400px;
  height: 20px;
  background-color: turquoise;
}
<div class="container1">
  <div class="grow1">
    <div class="container2">
      <div class="grow2a">Working (flex-basis)</div>
    </div>
  </div>
</div>

<div class="container1">
  <div class="grow1">
    <div class="container2">
      <div class="grow2b">Not working (width)</div>
    </div>
  </div>
</div>

revised fiddle 1


Solution for IE11

In IE11, contrary to spec guidance, the flex min-width / min-height default values are already 0, yet the flex item still breaks out.

The defaults are 0 because when the flexbox spec was first released, the min-* properties did not deviate from the CSS 2.1 initial values, which are 0.

Later, after browsers had completed their implementations, the flex min-* values were updated to auto. Chrome, Safari, FF and Edge made the update. IE11 did not.

The reason the flex items break out in IE11 relates to another issue: the browser wants an explicit width on the container

In your code, add flex-basis: 100% to .grow1.

More details here:

.container1 {
  margin-top: 10px;
  display: flex;
  width: 200px;
  height: 50px;
  background-color: red;
}

.grow1 {
  flex-grow: 1;
  height: 40px;
  background-color: green;
  flex-basis: 100%; /* NEW */
}

.container2 {
  display: flex;
  height: 30px;
  background-color: yellow;
}

.grow2a {
  flex-grow: 1;
  flex-basis: 400px;
  height: 20px;
  background-color: turquoise;
}

.grow2b {
  flex-grow: 1;
  width: 400px;
  height: 20px;
  background-color: turquoise;
}
<div class="container1">
  <div class="grow1">
    <div class="container2">
      <div class="grow2a">Working (flex-basis)</div>
    </div>
  </div>
</div>

<div class="container1">
  <div class="grow1">
    <div class="container2">
      <div class="grow2b">Not working (width)</div>
    </div>
  </div>
</div>

revised fiddle 2 (IE11)


More Browser Discrepancies

Evidence appears to exist (in this question and other examples I've seen) that Webkit-based browsers are no longer honoring the auto default defined in the spec.

Moreover, the adherence to the auto standard may vary based on which property is used for sizing: flex-basis vs. width / height

As discussed in the following post, these properties should render the same way.

Upvotes: 19

Related Questions