31

Considering the following example...

body {
  margin: 0;
}

* {
  box-sizing: border-box;
}

.parent {
  min-height: 100vh;
  width: 50vw;
  margin: 0 auto;
  border: 1px solid red;
  display: flex;
  align-items: center;
  justify-content: center;
}

.child {
  border: 1px solid blue;
  width: 150%;
}
<div class="parent">
 <div class="child">
 <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Amet tellus cras adipiscing enim eu turpis. Neque aliquam vestibulum morbi blandit. Sem integer vitae justo eget magna. 
 </div>
</div>

... I'm expecting the child to grow to width:150% and outgrow its parent in both left and right direction (as it's centered horizontally).

Why doesn't this happen?

Note: I'm interested in answers drawing from official or reliable sources, ideally pinpointing any bug or specification mentioning the behavior and any possible workarounds.

Debug info: Experiencing this in latest Chrome, Ubuntu 17.10. Haven't yet tested cross-browser, will update as I do.

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
tao
  • 82,996
  • 16
  • 114
  • 150
  • 5
    Why would it grow beyond its parent? – VXp Mar 26 '18 at 13:13
  • 4
    @VXp and why not ? – Temani Afif Mar 26 '18 at 13:15
  • 1
    Obviously default behavior. – VXp Mar 26 '18 at 13:19
  • 2
    The solution for your problem is use `min-width` instead of `width`, the official reason still don't know. – Chiller Mar 26 '18 at 13:20
  • 1
    @Chiller, after it's been answered and explained, it's easy to see why `min-width` "fixes" it: browsers won't allow `flex-basis` and `flex-shrink` to set an element's actual width below the set value of `min-width`, if specified. – tao Mar 26 '18 at 16:09
  • 2
    @TemaniAfif Because you don't expect a container to contain something bigger than itself. Allowing that *by default* is sheer madness in terms of being able to understand the layout; it reduces you ability to predict the actual rendering size to your ability to know the size of every single object being rendered. – jpmc26 Mar 26 '18 at 20:56
  • 2
    @TemaniAfif In other words, the restriction gives me the ability to rely on abstractions. That lets me consider an entire group of things as a whole, which makes it easier to understand what's happening and predict it. – jpmc26 Mar 26 '18 at 21:01
  • 1
    @jpmc26 `Because you don't expect a container to contain something bigger than itself` --> this is the default behavior if you don't use the Flexbox technique. I think the decision came from the fact that by default we can face unwanted overflow (padding, width:100% without box-sizing, etc). So they decide to avoid this overflow and if you want you can activate it again. But after all it remain the specification – Temani Afif Mar 26 '18 at 21:04
  • 1
    @TemaniAfif Yes, I realize that has not historically been the case, and this situation led to the consternation of many a developer. =) Which is why I'm glad it's been changed in flexbox. – jpmc26 Mar 26 '18 at 21:06

2 Answers2

34

You need to consider flex-shrink. As you can read here:

The flex-shrink CSS property specifies the flex shrink factor of a flex item. Flex items will shrink to fill the container according to the flex-shrink number, when the default size of flex items is larger than the flex container.

body {
  margin: 0;
}
* {
  box-sizing: border-box;
}

.parent {
  min-height: 100vh;
  width: 50vw;
  margin: 0 auto;
  border: 1px solid red;
  display: flex;
  align-items: center;
  justify-content: center;
}

.child {
  border: 1px solid blue;
  width: 150%;
  flex-shrink: 0; /* added this */
}
<div class="parent">
  <div class="child">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Amet tellus cras adipiscing enim eu turpis. Neque aliquam vestibulum morbi blandit. Sem integer vitae justo eget magna.
  </div>
</div>

And as we can read here also:

The flex-shrink property specifies the flex shrink factor, which determines how much the flex item will shrink relative to the rest of the flex items in the flex container when negative free space(1) is distributed.

This property deals with situations where the browser calculates the flex-basis values of the flex items, and finds that they are too large to fit into the flex container. As long as flex-shrink has a positive value the items will shrink in order that they do not overflow the container.

So by setting flex-shrink to 0 the element will never shrink thus you allow the overflow (by default the value is set to 1).


(1) Negative free space: We have negative free space when the natural size of the items adds up to larger than the available space in the flex container.



Worth to note that setting min-width: 150% will also give the expect result due to another flexbox feature that doesn't allow a flex item to shrink past its minimum width (either explicitely defined or intrinsically defined)

body {
  margin: 0;
}
* {
  box-sizing: border-box;
}

.parent {
  min-height: 100vh;
  width: 50vw;
  margin: 0 auto;
  border: 1px solid red;
  display: flex;
  align-items: center;
  justify-content: center;
}

.child {
  border: 1px solid blue;
  min-width: 150%;
}
<div class="parent">
  <div class="child">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Amet tellus cras adipiscing enim eu turpis. Neque aliquam vestibulum morbi blandit. Sem integer vitae justo eget magna.
  </div>
</div>

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

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 2
    more info on W3C [here](https://www.w3.org/TR/css-flexbox-1/#flex-flex-shrink-factor) – dippas Mar 26 '18 at 13:28
  • @AndreiGheorghiu i also added another intresting link that describe more this behavior – Temani Afif Mar 26 '18 at 13:32
  • @dippas and Afif. Even though now, that you pointed it out, it's clearly *"described-in-specs"*, I still find it quite counter-intuitive: I mean, why would one need to set `flex-shrink` to a particular value when they want the child to ***grow*** past parent width? When teaching flexbox to a student, one would need to start with: *"What you're about to hear doesn't make much sense, but..."* Right? It's the very last sentence of the second quote that gives the actual reason, but it will be tricky for future students to get. A flexbox gotcha. :) – tao Mar 26 '18 at 13:35
  • 1
    @AndreiGheorghiu i agree it's conter-intuitive at the first ... but what if we think differently ? people always complaining about overflow due to width:100% + padding when they forget box-sizing, or some border or margin, etc etc ... so i think Flexbox come with this solution to initially avoid this and by default they set flex-shrink to 1 ... then you have to desactivate it if you want – Temani Afif Mar 26 '18 at 13:38
  • @AndreiGheorghiu yes right, even in the Spec they don't specify the word "do not oveflow", they simply explain the property .. so yes it's a tricky small hidden thing you need to catch ! – Temani Afif Mar 26 '18 at 13:40
  • 3
    I now fully understood. Flex starts from `width`, from which it creates `flex-basis`. It then tries to fit all flex-children in flex-parent. Since the child can shrink, it gets shrunk and it will only remain at the value of `width` if it can't be shrunk. – tao Mar 26 '18 at 13:43
  • @AndreiGheorghiu I think it's relevant (in terms of intuitive behaviour) that HTML Tables do the same thing: if you set a width for the table, it acts as a limit to the table's size, regardless of how wide each of the cells within it are. The cell can't go "outside" the table, and if it resized the table, it would no longer be 150% of the new width; so instead, the cell shrinks based on the other constraints of the table. – IMSoP Mar 26 '18 at 15:16
-1

flex-shrink formula for (children) A & B inside P (parent) :

  • note: when widthA = 100% => widthA= 100% * widthP

X = widthP / (widthA + widthB) ;

newA = widthA * X ; //widthA after shrink

newB = widthB*X ; //widthB after shrink

AJ Zack
  • 205
  • 1
  • 8