Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

html

css

flexbox

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>
like image 488
chris Avatar asked May 12 '17 09:05

chris


People also ask

How do you shrink a flex item?

The flex-shrink CSS property sets the flex shrink factor of a flex item. If the size of all flex items is larger than the flex container, items shrink to fit according to flex-shrink .

How do I make my flexbox not stretch?

By default, the child elements of a flexbox container will stretch vertically to fill the height of the container. This can be prevented by using the align-self property on the child element that you do not want to stretch.

How do I fix my flexbox size?

A flexbox item can be set to a fixed width by setting 3 CSS properties — flex-basis, flex-grow & flex-shrink. flex-basis : This property specifies the initial length of the flex item. flex-grow : This property specifies how much the flex item will grow relative to the rest of the flex items.

How can you make a flexbox container box wrap to the next line on a smaller screen?

Here's the solution: When the screen resizes smaller than 990px, allow flex items to wrap and give the first item a 100% width, which forces the following items to the next line.


1 Answers

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:

  • Why doesn't flex item shrink past content size?

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:

  • Why IE11 doesn't wrap the text in flexbox?
  • flexbox flex-basis: 0px in Chrome

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

  • What are the differences between flex-basis and width?
like image 127
Michael Benjamin Avatar answered Oct 06 '22 14:10

Michael Benjamin