How to create a flex item that shrinks before it wraps? - html

As can be seen in this JS-Fiddle, I basically try to use this CSS to create two divs that should fullfill these requirements:
If twice the space of the wider item is available, both should use 50% width (that works)
If not enough space for both items is available, they should wrap (that works)
If enough space is available for both items, but less than twice the width of the wider one, the narrower item should shrink (that does NOT work, it wraps)
I don't understand this behavior, because I have set flex-shrink for the flex items, so they should be able to shrink - but they don't: If the narrower item would be less than 50% wide, it wraps.
.m {
display: flex;
flex-wrap: wrap;
}
.l_1 {
background-color: red;
flex: 1 1 50%;
}
.r_1 {
background-color: yellow;
flex: 1 1 50%;
}
<div class=m>
<div class=l_1>
left_______________________________________________X
</div>
<div class=r_1>
right
</div>
</div>
(Tested on Firefox and Chrome)

The problem is not flex-shrink. The problem is flex-basis. You have it set to 50%.
This means that flex-grow will add free space to the flex-basis, and each item will wrap quickly, before it has an opportunity to shrink.
Switch from flex-basis: 50% to flex-basis: 0.
More specifically, instead of flex: 1 1 50% use flex: 1, which breaks down to this:
flex-grow: 1
flex-shrink: 1
flex-basis: 0
Now flex-grow distributes free space equally – not proportionally – to both items, and they can shrink before they wrap.
(Here's a more in-depth explanation: Make flex-grow expand items based on their original size)
.m {
display: flex;
flex-wrap: wrap;
}
.l_1 {
background-color: red;
flex: 1;
}
.r_1 {
background-color: yellow;
flex: 1;
}
<div class=m>
<div class=l_1>left_______________________________________________X</div>
<div class=r_1>right</div>
</div>
revised fiddle

When wrapping is enabled, it takes the place of shrinking, so where there is a condition that would trigger shrinking, it wraps instead - until there is only one item in the row, and if that's still bigger than the container, then it will shrink.
So, you need to set flex-basis for all boxes to the minimum size that block should be before wrapping. Note: a box will never shrink further than its minimum content width, meaning you can set flex-basis to 0 and it will go by the minimum content width of each box.
Then, if you want the boxes to expand to fill the available space, then use the flex-grow property (first value in flex) to control the relative amount by which each one should grow.

Flex-shrink will not apply with flex-wrap: wrap also applied. It will line wrap instead.
The only exception to this is when you only have one flex item in the row. Then it should allow for flex-shrink to apply.
Perhaps the appropriate fix is to only apply the flex-wrap within a media query, so it only happens in smaller viewports?

Related

Does justify-content of parent matter when all children have a flex property?

.parent {
background-color: yellow;
display: flex;
justify-content: space-evenly;
}
.parent > div {
background-color: lightblue;
flex: 1;
margin: 5px;
}
<div class="parent">
<div>Child #1</div>
<div>Child #2</div>
<div>Child #3</div>
</div>
In the above simple example, there is a parent div with 3 children divs. All the children have a flex: 1 property and so they are all distributed equally inside their parent. At this point, does the property justify-content: space-evenly of the parent actually count? No matter what value I insert, the result is always the same. Could it be deleted at all?
The justify-content property aligns flex items along the main axis of the current line of the flex container. This is done after any flexible lengths and any auto margins have been resolved. Typically it helps distribute extra free space leftover when either all the flex items on a line are inflexible, or are flexible but have reached their maximum size. It also exerts some control over the alignment of items when they overflow the line.ref
If you have no extra space then you can safely omit the property and nothing will change.
It can make a difference in two situations:
Your layout is dynamic and your item may have a reduced size and a free space is created
You are using justify-content:inherit inside a flex item to get the parent value (not a very common situation by the way)

Flex-shrink not working as expected

I'm starting to work with flexbox, and, in order to understand flex-grow and flex-shrink, I used a simple program that displays two blocks and makes them take up the whole width using flex-grow: 2 in one of them and flex-grow: 1 in the other.
If I check the following line in the console: $(".one").width() === $(window).width() /3 it returns true. So far, so good.
The problem appears when I reduce the window size, because as soon as I do this the same line in the console ($(".one").width() === $(window).width() /3) starts returning false.
I know the default value for flex-shrink is 1. Wouldn't that mean that the proportions between both blocks would be maintained (since they are both being shrunk by the same amount)? Can anyone explain why this result happens?
Here's my code:
* {
font-family: verdana;
margin: 0;
}
body {
background: #eee;
}
.wrapper {
width: 100%;
max-width: 2000px;
margin: 0 auto;
}
.flex-container {
display: flex;
background-color: white;
}
.box {
height: 100px;
}
.one {
background-color: red;
flex-grow: 1;
}
.two {
background-color: blue;
flex-grow: 2;
}
<div class="wrapper">
<div class="flex-container">
<div class="box one"></div>
<div class="box two"></div>
</div>
</div>
While not relevant to your question it might be worth noting:
flex-wrap takes precedence over flex-shrink, unless the item is wider than the container.
So if you have flex-wrap enabled and you're expecting two items to shrink to fit side by side on a single row they won't, they'll wrap first even if there's plenty of shrink-potential.
If the item is wider than the parent container it can't wrap so it will shrink if it can.
You'd have to solve this with min/max widths, make the initial size smaller (this is probably the best way) or creating a new parent container without flex-wrap.
See also Is there any use for flex-shrink when flex-wrap is wrap?
flex-shrink is designed to distribute negative free space in the container.
In other words, it only works when flex items are big enough to overflow the container.
You're not having that problem here. There is no negative space. Therefore, I don't believe flex-shrink is having any effect on your layout.
flex-grow is consuming the positive free space and seems to be working fine.
You would need to set a flex-basis or add content to the items to put flex-shrink in play.
https://www.w3.org/TR/css-flexbox-1/#flex-property
This is related to float calculations. Your flex code is working perfectly fine, the problem arises from the arithmetic operation, where the width of the container might not perfectly divide to 3, so the result will be a floating number which might or not be rounded to the closest number, so that's why width of your first flexbox item might not be equal to width / 3 because of that rounding.
Tested with Chrome Inspector.
Take a look at https://css-tricks.com/snippets/css/a-guide-to-flexbox/#article-header-id-13
They suggest using the shorthand flex: [number]; because it intelligently sets the default flex-shrink to 0. Just because the default for flex-shrink is 1 doesn't mean that 1 is what you want. I haven't been using flexbox that long, but I've yet to come across a scenario in which I've had to specify a flex-shrink. 0 has been working for me thus far. Maybe somebody else can provide a scenario for using it.
TLDR
Use flex attribute instead of flex-grow

`flex-basis: auto` sizes parent as if child were `flex-basis: auto`, when child is `flex-basis:10px`

For example, you shouldn't be able to see the red of the parent here, but you do, because parent: 0 0 auto is sizing the parent to the auto width of its child content. You can see clearly though, the real width of its content is 10px, so shouldn't its auto sizing make the parent 10px as well?
body{
display:flex;
}
#parent {
flex: 0 0 auto;
background-color: red;
}
#child {
flex: 0 0 10px;
background-color: grey;
}
div{ display:flex; min-width:0; min-height:0; overflow:hidden; } /*There's some weirdness described here, http://stackoverflow.com/questions/36247140/why-doesnt-flex-item-shrink-past-content-size where flex elements default to min-width:auto, which could have caused problems here, but this doesn't change anything, so apparently this is not the issue*/
<div id="parent">
<div id="child">childcontents</div>
</div>
This occurs in firefox and chrome, so presumably this is going to turn out to be correct somehow. I'd like to know how, so that I can stop it.
According to my understanding of the spec, this is a bug.
What Michael_B said is correct, first #parent is sized, and once its width is know, #child can flex. Since flexing usually involves growing or shrinking, the size of the flex container must be known before flexing the flex item; and the final size of the flex item may not be the flex basis, so the flex container shouldn't be sized using that value.
The solution is easy: use width instead of flex-basis. width does not flex, so it doesn't depend on the width of the container. Thus the container can use the width of their contents when sized.
body {
display: flex;
}
#parent {
flex: none;
background-color: red;
}
#child {
width: 10px;
flex: none;
background-color: grey;
}
div {
display: flex;
min-width: 0;
min-height: 0;
overflow: hidden;
}
<div id="parent">
<div id="child">childcontents</div>
</div>
That said, in your case using flex-basis should work. That's because your flex item has both a zero flex grow factor and a zero flex shrink factor. It cannot grow nor shrink, it becomes directly frozen. Therefore it's possible to use consider the flex-basis when sizing the parent, and the spec says so:
9.9.3. Flex Item Intrinsic Size Contributions
The main-size min-content/max-content contribution of a
flex item is its outer min-content/max-content size,
clamped by its flex base size as a maximum (if it is not
growable) and/or as a minimum (if it is not shrinkable), and then
further clamped by its min/max main size properties.
The contribution of the ungrowable unshrinkable flex item is clamped by its flex base size both as a maximum and as a minimum. That is, the contribution is exactly the flex base size, which is defined as the flex basis when the flex basis is definite.
It looks like the flex layout algorithm calculates the width of the flex container before arriving at the width of the flex items.
Hence, it determines the auto size of #parent based on the full width of #child.
Then it sizes #child to flex-basis: 10px.
At his point, however, the auto width of the parent has already been determined and is inflexible.
Testing in Chrome, re-arranging the rules makes no difference, so it doesn't appear to be a cascading issue.
This is my view of the behavior without an official reference to back it up. You may find the precise answer here: W3C Spec: Flex Layout Algorithm

min-width rendering differently in flex-direction: row and flex-direction: column

I have a container with display: flex and flex-direction: row.
In that container there is a sub-container also with display: flex but with flex-direction: column.
The problem is if I add an input in the sub-container, the min-width of that input will be ignored.
This is the code where I tried several cases of input in flexbox:
form {
margin: 100px;
}
div.flex_ctn {
display: flex;
}
input {
flex: 1;
min-width: 40px;
}
div.column {
flex-direction: column;
}
div.row {
flex-direction: row;
}
div.sub_ctn {
flex: 1;
/*min-width:40px;*/
}
<form>
<div class="flex_ctn row">
<input />
</div>
<div class="flex_ctn column">
<input />
</div>
<div class="flex_ctn row">
<div class="flex_ctn column sub_ctn">
<input />
</div>
</div>
<div class="flex_ctn column">
<div class="flex_ctn row sub_ctn">
<input />
</div>
</div>
</form>
https://fiddle.jshell.net/s3gu32ku/2/
If you reduce the screen size, the 3rd line doesn't react like the others.
In the css you will see that the last line is set as comment. When that part is enabled you just have to reload and the issue disappears. So, perfect ! I have got the solution!
But that bothers me to use something that I don't understand ^^.
This would be great if someone can explain to me why that error occurs, why that line fix it, and also if there a better way to avoid that issue.
Generally speaking, flex items, by default, cannot be smaller than the size of their content.
More specifically, these are initial settings of flex items:
min-width: auto (applies in flex-direction: row)
min-height: auto (applies in flex-direction: column)
Even more specifically, take a look at the spec language:
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.
auto
On a flex item whose overflow is visible in the main axis, when
specified on the flex item's main-axis min-size property, specifies an
automatic minimum size. It otherwise computes to 0.
In other words, the minimum sizing algorithm applies only on the main axis.
Your input elements in column-direction containers don't get min-width: auto – because the main axis is vertical in those cases – so they shrink and won't overflow the container. You can see this behavior play out on your second input element. Reduce the screen size while viewing this demo.
The same thing happens with the third input, which is a child of a nested flex container with flex-direction: column... EXCEPT, this column-direction container is also a flex item of larger container with flex-direction: row.
This means the main axis of the nested container is horizontal and min-width: auto applies. As a result, this flex item will not shrink below the intrinsic width of the input. For an illustration, see the same demo from above.
Therefore, you need to override this default with min-width: 0 or overflow: hidden (demo).
And for the reasons explained above, the fourth input, contained in a nested row-direction flex container, will also need to have min-width: auto overridden (demo).
Related: Why doesn't flex item shrink past content size?

flex items ignoring width

I've reordered some elements in my html using flexbox in the responsive design of a website which works fine but the elements then won't resize properly.
At a breakpoint I have applied a class of flex to the home-promos div and reordered the elements. This works correctly.
The problem then arises when I try to resize the div's to percentage widths. They will only resize up to a certain point, such as 50% and then won't get any bigger.
Is anyone who is better with flexbox than myself able to tell me what the issue is?
.home-promos {
display: flex;
}
.home-promo-center {
order: 1;
}
.home-promo-left {
order: 2;
}
.home-promo-right {
order: 3;
}
<div class="home-promos">
<div class="home-promo-left">
<div class="promo-left-content">
*content*
</div>
</div>
<div class="home-promo-center">
<div class="promo-center-content">
*content*
</div>
</div>
<div class="home-promo-right">
<div class="promo-right-content">
*content*
</div>
</div>
</div>
When you create a flex container (display: flex or display: inline-flex), it comes with several default settings. Among them are:
flex-direction: row - flex items will align horizontally
justify-content: flex-start - flex items will stack at the start of the line
flex-wrap: nowrap - flex items are forced to stay in a single line
flex-shrink: 1 - flex items are allowed to shrink
Note the last two settings.
Your three divs are forced to remain in a single line. Hence, their combined width is limited to the width of the container.
Also, they are allowed to shrink, which prevents them from overflowing the container. This also limits their width.
To apply whatever width you want to each flex item you can override these initial settings with:
flex-wrap: wrap - now there's more space because flex items can break to new lines
flex-shrink: 0 - now there's more space because flex items will not shrink and can overflow their container if necessary