Understanding flex-grow and flex-shrink when using flex-basis - css

I'm trying to understand the following line.
flex: 0 1 50%
Now if the last value, flex basis was pixels the above would say that the item is not allowed to grow, but allowed to shrink and will be at maximum 50 pixels.
But with percentage in there instead, what are the relations. It will be maximum 50% of width, but eh, since it is not allowed to grow it will stay at 50 percent of...something
Curious what your interpretation is.
Thanks in advance, as we say in Sweden.

Percentage lengths are relative to their containing blocks.
Therefore, if the flex container has a width of 200px, and the flex items are set to flex-basis: 50%, then each item will resolve to 100px.
Of course, in flex layout, flex-basis represents the initial main size or, the size before flex-grow and flex-shrink are applied.
You have flex-grow disabled, so nothing happens there.
But you have flex-shrink enabled, so the items will shrink below 100px when necessary to prevent an overflow of the container.
In this case, because all items are set to flex-shrink: 1, they will shrink in equal proportion.
article {
display: flex;
width: 200px;
border: 1px solid black;
}
[one] > section {
flex: 0 1 50px;
}
[two] > section {
flex: 0 1 50%;
}
[three] > section {
flex: 0 0 50%;
}
/* non-essential demo styles */
section {
height: 50px;
background-color: lightgreen;
border: 1px solid #ccc;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
<p>container width 200px in all cases</p>
<article one>
<section><span>50px</span></section>
<section><span>50px</span></section>
<section><span>50px</span></section>
<section><span>50px</span></section>
</article>
<hr>
<p><code>flex-shrink</code> enabled</p>
<article two>
<section><span>50%</span></section>
<section><span>50%</span></section>
<section><span>50%</span></section>
<section><span>50%</span></section>
</article>
<hr>
<p><code>flex-shrink</code> disabled</p>
<article three>
<section><span>50%</span></section>
<section><span>50%</span></section>
<section><span>50%</span></section>
<section><span>50%</span></section>
</article>
More details about percentages and flex-basis:
§ 7.2.3. The flex-basis
property
Percentage values of flex-basis are resolved against the flex item’s
containing block (i.e. its flex container); and if that containing
block’s size is indefinite, the used value for flex-basis is
content.
More details about percentage lengths in general:
Working with the CSS height property and percentage values

Related

Why flexbox won't grow to its text?

I need .child-1-2 to grow to its text, but the text overflows. When I change flex-basis of .child-1-1 from 50px to auto, it seems to work. Why is that happening?
.parent-1 {
display: flex;
}
.child-1 {
display: flex;
flex: 0 0 auto;
background: #4c72af;
}
.child-1-1 {
display: flex;
flex: 0 0 50px;
}
.child-1-2 {
display: flex;
flex: 1 0 auto;
}
.child-2 {
display: flex;
flex: 1 0 auto;
background: #f7ed7e;
}
<div class="parent-1">
<div class="child-1">
<div class="child-1-1">C1</div>
<div class="child-1-2">Some text</div>
</div>
<div class="child-2">
<div class="child-2-1">Another text</div>
</div>
</div>
In order to understand the reason why the described behavior takes place, we should know how flex-basis and flex-grow actually work and how width of flex items is calculated.
Flex-grow
From flex-grow is weird. Or is it?
If we apply display: flex; to the parent element and don't change
anything else, the child elements will be stacked horizontally, no
matter what. If there isn't enough space, they will shrink in size. If
on the other hand there is more than enough space, they won't grow,
because Flexbox wants us to define how much they should grow. So
rather than telling the browser how wide an element should be,
flex-grow determines how the remaining space is distributed amongst
the flex items and how big the share is each item receives.
Flex-basis
Width of a flex item is determined in the following order:
content
width
flex-basis (limited by max|min-width)
From The Difference Between Width and Flex Basis
If no flex-basis is specified, then the flex-basis falls back to the
item’s width property.
If no width is specified, then the flex-basis falls back to the
computed width of the item’s contents.

Stretch child to 100% width and height without changing the child's rules

When parent is display: flex;, is there a way to force it's children to stretch itself to 100% of the parent's width and height, without accessing the children's props?
i've found align-items: stretch; for vertical stretching, but can't find something for the horizontal.
"I've found align-items: stretch; for vertical stretching, but can't find something for the horizontal"
I guess you're searching for justify-content property, but it doesn't have stretch rule, it only can accept the following rules: justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;
But
You can specify flex-grow: 1 for child element and it will fill all available space of the parent.
Here is the example (I have added paddings to parent element just for example, so you can see the difference between parent and child):
.wrapper {
display: flex;
width: 80%;
height: 300px;
background-color: orangered;
padding: 8px;
}
.item {
flex-grow: 1;
background-color: forestgreen;
}
<div class="wrapper">
<div class="item"></div>
<div>
is there a way to force it's children to stretch itself to 100% of the
parent's width and height, without accessing the children's props?
No, as for an i.e. flex row item (the default), its default width is auto, which make it depend/size by its content, and for it to stretch horizontal (fill the remaining space), it needs flex-grow: 1.
The same goes for flex column item, where the flex-grow: 1 will make it fill the parent's height.
i've found align-items: stretch; for vertical stretching, but can't
find something for the horizontal
When it comes to align-items and its default stretch, it affect the cross axis, and will for i.e a flex row item, stretch it to fill its parent's height, and for a flex column item it is the other way around, where it fill its parent's width.
There is no property for the main axis that does the same, as that is what the flex property is for, here as shorthand flex: <flex-grow> <flex-shrink> <flex-basis>, and since one might want different behavior for different items, it is set on the flex item (what you call the child)
Here is a flex row samples using flex-grow: 1
.parent {
display: flex;
height: 200px;
background: red;
}
.child {
flex-grow: 1;
background: blue
}
<div class='parent'>
<div class='child'></div>
</div>

Why flex container `flex: 1 0 0px` collapses on Chrome?

On Chrome I encountered Flexbox behaviour I don't understand. When a flex child which is also a flex container has flex: 1 0 0px, it collapses itself and it's contents.
Even though flex-basis is set to 0px, as far as I understand setting flex-grow to 1 (first number in flex shorthand) should make the item grow, when needed.
In my example .bottom-container has height set to 300px. That height is respected on Firefox, but collapsed to 0px on Chrome. Why?
.top-container {
display: flex;
flex-flow: column nowrap;
}
.middle-container {
flex: 1 0 0px;
display: flex;
flex-flow: column nowrap;
}
.bottom-container {
flex: 0 0 auto;
height: 300px;
}
<div class="top-container">
<div class="middle-container">
<div class="bottom-container"></div>
</div>
<div class="middle-container">
<div class="bottom-container"></div>
</div>
</div>
The problem is with the flex-basis component.
When you have flex-basis: 0, Chrome and Firefox compute to flex-basis: 0px.
However, the pixel value breaks your layout in Chrome.
Instead, for cross-browser compatibility, use this:
flex: 1 0 0%
Ok, so here's the logic.
You haven't specified a height of .top-container so it's child elements (.middle-container) cannot grow, because there is no room for them to grow into, despite having flex: 1 0 0 and, therefore, .middle-container elements will always maintain a height of 0.

Flexbox grow and shrink property columns vs determined width columns for responsive grid system

Lately I've been learning flexbox and how to make my own grid system. When making grid system using floats, we determine number of columns per layout and each column's width in percentages. But when using flexbox, all the layout tutorials I saw are simply using flex-direction: row; and flex: 1
for columns, to make all of them equal size, equal gutter, centered and in one row. But when I checked flexboxgrid source code on github, they are using same principle as bootstrap 3. They have columns for different screen sizes, 12 columns and flex-grow, shrink are disabled. Instead, each column is determined in width percentages, like col-xs-1 max-width: 8.33%.
Now I'm wondering what's the difference between these two techniques and which one is more preferable. I mean determining width for each column requires a lot of counting, while using flex grow property just fulfills the whole viewport in main axis with equally sized columns and gutters.
tl;dr
They are not techniques to achieve the same result, they do different things.
Flexbox grid uses flex-basis to determine width in flex container's main axis. It does not use flex: 1; on flex items because that is equivalent to flex: 1 1 0;. Which means flex-basis would have a value of 0, and the flex items sizes would be proportional to the specified grow and shrink factor, both having a value of 1.
Example
col-xs-1 with a flex-basis of 0 specified from flex: 1; would grow as if it was col-xs-12 if it is the only child, if there is another col-xs-1 like this as a sibling, then it would grow as if it was col-xs-6 and so forth.
It is expected for every col-xs-1 to fill 1/12, (8.33333333%), of the container, which would not be the case using flex: 1;.
* {
box-sizing: border-box;
}
article {
margin-bottom: 1rem;
}
[class^="col-"],
[class*="col-"] {
flex: 0 0 auto; /* flex-grow: 0; flex-shrink: 0; flex-basis: auto; */
}
.row {
display: flex;
margin-right: -.5rem;
margin-left: -.5rem;
}
.col-xs-1 {
padding-right: .5rem;
padding-left: .5rem;
flex-basis: 8.33333333%;
}
.box-row {
min-height: 1em;
background: #007FFF;
}
article:last-of-type .col-xs-1 {
flex: 1; /* Same as flex: 1 1 0; */
}
<article class="row">
<section class="col-xs-1">
<div class="box-row"></div>
</section>
<section class="col-xs-1">
<div class="box-row"></div>
</section>
</article>
<article class="row">
<section class="col-xs-1">
<div class="box-row"></div>
</section>
<section class="col-xs-1">
<div class="box-row"></div>
</section>
</article>

Why is the default value of flex-basis not 'auto'?

.wrapper {
display: flex;
flex-flow: row wrap;
}
.aside-1 {
width: 300px;
flex: 1;
background: gold;
}
.aside-2 {
flex: 1;
background: hotpink;
}
<div class="wrapper">
<aside class="aside-1">Aside 1</aside>
<aside class="aside-2">Aside 2</aside>
</div>
In this case, the width of .aside-1 should be longer than the one of .aside-2 if the flex-basis of .aside-1 was 'auto'.
But it seems being 0%, so the width of .aside-1 and .aside-2 are the same.
The answer is found in the docs here:
<‘flex-basis’>
This component sets the flex-basis longhand and specifies the flex basis: the initial main size of the flex item, before free space is distributed according to the flex factors. It takes the same values as the width property (except auto is treated differently) and an additional content keyword. When omitted from the flex shorthand, its specified value is 0%.
(emphasis mine)
In your example, the flex-basis value is correctly set at 0% when not defined in the flex shorthand.

Resources