The following is a guest post by Manuel Matuzovic. It illustrates how flex-grow works, weird quirks and all. Then he goes into several examples on how common layout patterns may be implemented using flex-grow
and flex-basis
.
When I found out about flex-grow
, I made a simple demo to find out what it did and how it worked.
I thought I got everything figured out, but when I tried it on a website a colleague has recently made, nothing worked as expected. No matter what we did, the layout didn’t look and work like it did in my demo. That got me thinking and I started to doubt that I knew what flex-grow
was all about.
How flex-grow doesn’t work
Before we are going to take a deep dive into the functionality of flex-grow
, I want to explain to you what I got wrong at first.
I thought that all flex items with flex-grow
set to 1
would have an equal width. If one of the items had flex-grow
set to 2
that item would be twice as big as all the others.
This sounds great. It seems as if this is exactly what’s happening in the Pen I mentioned before. The parent element is 900px wide, the section with flex-grow: 2
has a calculated width of 600px and the aside element with flex-grow: 1
has a calculated width of 300px.
As you can see, everything works out perfectly in this demo, but it did not work at all in a real life example, even though we used the exact same CSS. As it turns out, the problem wasn’t the CSS, but the content (or lack of content). The demo I made works, because by only using two empty elements I created a test case that was too simple to depict the important specifics of the property.
flex-grow
really works
How I just described how flex-grow
does not work, but I showed you a demo that actually does do what I claim it does not (Later on in this article I will explain the reason for this.).
To clarify things, let me show you another Pen. We have got the exact same setup as in the first pen, but this time the section and aside elements aren’t empty. Now the ratio isn’t 2:1 anymore and the element with flex-grow set to 1 is actually bigger than the element with flex-grow set to 2.
Explanation
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.
Or in other words:
A flex container distributes free space to its items (proportionally to their flex grow factor) to fill the containers, or shrinks them (proportionally to their flex shrink factor) to prevent overflow.
https://drafts.csswg.org/css-flexbox/#flexibility
Demonstration
The concept is much easier to understand if we visualise it.
First we set the display
property of our parent element to flex
and by doing that our child elements become flex items and are positioned horizontally next to each other.
Next we decide how many shares of the extra space each element receives. In our previous example the first element receives 2/3 of the remaining space (flex-grow: 2
) and the second element 1/3 (flex-grow: 1
). By knowing how many flex-grow values we have in total, we know by which number to divide the remaining space.
Finally we have the number of distributable pieces. Each element receives the appropriate number of pieces based on its flex-grow
value.
Calculation
Theory and visual representations are nice, but let’s get dirty and do the math for the above example.
For our calculation we need 4 numbers: The parent width, the initial width of our section and our aside element and the total number of flex-grow values we’ll use.
parent width: 900px
section width: 99px
aside width: 623px
total flex-grow values: 3
1. First we have to calculate the remaining space
That’s pretty easy. We take the parents width and subtract the total initial width of every child element.
900 - 99 - 623 = 178
parent width – initial section width – initial aside width = remaining space
2. Next we have to determine how much one flex-grow
is
Now that we have the remaining space we need to determine into how many slices we want to cut it. The important thing here is that we don’t divide the remaining space by the number of elements, but by the number of total flex-grow values. So in our case that’s 3 (flex-grow: 2
+ flex-grow: 1
)
178 / 3 = 59.33
remaining space / total flex-grow values = “one flex-grow”
3. Finally the sliced up remaining space gets distributed between all elements
Based on their flex-grow values the section receives 2 slices (2 * 59.33) and the aside 1 (1 * 59.33). These numbers are added to the initial width of each element.
99 + (2 * 59.33) = 217.66 (≈218px)
initial section width + (section flex-grow value * “one flex-grow”) = new width
and
623 + (1 * 59.33) = 682.33 (≈682px)
initial aside width + (aside flex-grow value * “one flex-grow”) = new width
Easy cheesy, right?
Alright, but why does the first demo work?
We have the formula, now lets find out why we actually got the numbers we wanted in the first Pen even though we didn’t know what we were doing.
parent width: 900px
section width: 0px
aside width: 0px
total flex-grow values: 3
1. Calculate the remaining space
900 - 0 - 0 = 900
2. Determine how much one flex-grow is
900 / 3 = 300
3. Distribute the sliced up remaining space
0 + (2 * 300) = 600
0 + (1 * 300) = 300
If the width of each element is 0, the remaining space equals the actual width of the parent element and thus it looks like flex-grow
divides the parent element’s width in proportional parts.
flex-grow
and flex-basis
Just a quick recap: flex-grow
will take the remaining space and divide it by the total amount of flex grow values. The resulting quotient is multiplied by the respective flex-grow
value and the result is added to each child elements initial width.
But what if there’s no remaining space or what if we don’t want to rely on the elements initial width, but on a value we set? Can’t we still use flex-grow
?
Of course we can. There is a property called flex-basis
which defines the initial size of an element. If you use flex-basis
in conjunction with flex-grow
the way the widths are calculated changes.
<‘flex-basis’>
This component sets theflex-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.
https://drafts.csswg.org/css-flexbox/#valdef-flex-flex-basis
The big difference if we apply flex-basis
to an element is that we don’t use its initial width in our calculation anymore, but the value of its flex-basis
property.
I have adapted our previous example by adding flex-basis
to each element. Here’s the Pen for you.
parent width: 900px
section width: 400px (flex-basis value)
aside width: 200px (flex-basis value)
total flex-grow values: 3
1. Calculate the remaining space
900 - 400 - 200 = 300
2. Determine how much one flex-grow is
300 / 3 = 100
3. Distribute the sliced up remaining space
400 + (2 * 100) = 600
200 + (1 * 100) = 300
Just for the sake of completeness, you don’t have to use pixel values and hope for the best, percentages work as well.
Working with the box model
So to cover everything, let’s check out what happens if we add padding and margin. Nothing too special actually. In the first step of the calculation you just have to remember to subtract the margins as well.
The only thing to note is that in terms of box-sizing
flex-basis
behaves like the width
property. That means that the calculation as well as the results change if the box-sizing
property changes. If box-sizing
was set to border-box
, you would only work with the flex-basis
and margin
values in your calculation, because the padding
is already included in the width.
Some useful examples
Alright, enough with the math. Let me give you some examples of how you’re able to establish flex-grow
effectively in your projects.
width: [ x ]%
No more Due to the fact that the remaining space gets distributed automatically, we don’t need to think about width values anymore, if we want our child elements to fill the parent element.
See the Pen QyWEBb by Manuel Matuzovic (@matuzo) on CodePen.
The “Holy Grail” 3 column liquid layout with pixel-widths
Mixing fixed and fluid widths in column layouts is possible with float, but it is neither intuitive or easy nor flexible. Of course with Flexbox and a little flex-grow and flex-basis magic that is a piece of cake.
See the Pen jbRjMG by Manuel Matuzovic (@matuzo) on CodePen.
Filling remaining space with any element
If you, for example, have an input field next to a label and you want the input field to fill the rest of the space, you don’t need ugly hacks anymore.
See the Pen eJYdWV by Manuel Matuzovic (@matuzo) on CodePen.
You can find more examples on Philip Waltons Solved by Flexbox.
Listening to the specs
According to the specs, we should use the flex
shorthand rather than flex-grow
directly.
Authors are encouraged to control flexibility using the
flex
shorthand rather thanflex-grow
directly, as the shorthand correctly resets any unspecified components to accommodate common uses.
https://drafts.csswg.org/css-flexbox/#flex-grow-property
But be careful! If you just use flex: 1;
some of the above examples won’t work anymore, because the values set for the common uses don’t equal the default values and thus interfere with our needs.
If you want to use flex
for our use case, you should define it like this:
flex: 2 1 auto; /* (<flex-grow> | <flex-shrink> | <flex-basis>) */
Learning Flexbox
If you want to learn more about Flexbox, please check out these great resources:
- A Complete Guide to Flexbox by Chris Coyier
- Flexbox adventures by Chris Wright
- Flexbox Froggy by Thomas Park
- What the Flexbox? by Wes Bos
- flexboxin5
- Flexbox Tester by Mike Riethmuller
Summary and lessons learned
Is flex-grow
weird? Nah, not at all. We just have to understand how it works and what it does. If an element has flex-grow
set to 3
it does not mean that it’s 3 times bigger than an element that has flex-grow
set to 1, but it means that it gets 3 times more pixels added to its initial width than the other element.
I learned my lesson by testing flex-grow with two empty elements, which gave me a completely different understanding of what the property actually does and this of course led to wrong conclusions. You should check out new stuff in an environment that is as realistic as possible, to get the best impression of how it really works and behaves.
Thanks for sharing, it is very tampting to use flex CSS but is there a guerantee that it will be supported in all browsers ?
Is there an end to all the hack$ ;)
There has been support across all modern browsers for years. This doesn’t mean that there is no more need for hacks, since flexbox can be quite buggy.
You are welcome! If you progressively enhance your websites and use Modernizr for feature detection, I’d say you’re good to go (http://caniuse.com/#search=Flexbox). With the current version of Modernizr you are able to detect support for the current implementation of Flexbox, the old one and the one that lacks support for flex-wrap.
Thanks for this post, Manuel!
Another fantastic way to learn all things Flexbox is with Landon Schopp’s eBook, Unraveling Flexbox.
You’re welcome! Thank you, looks promising, I will check it out.
Great post, thank you!
Thank you, I appreciate it!
Hi,
Nicely explained article,
Something you may wish to correct is that you made a similar mistake to your original in your holy grail layout (the second time that CSS Tricks has got this wrong by the way ;)) in that you have not added enough content into your section element to see what the full effect will be like.
You will find that your 150px aside columns do not remain at 150px but squash when the middle column has more content.Y ou need to set flex-shrink to zero.
”’aside {
flex-basis: 150px;
flex-shrink:0;
}
”’
Otherwise good job.
You’ve got me there ;) Thank you for your feedback! I have updated the Pen.
Hi,
in the 3 column liquid layout example, is there a difference between using “flex-basis:150px” and simply using “width:150px”?
Thanks, and great article by the way ;)
Flex-basis applicable always along main axis of flex-container, width only for horizontal axis of element. You’re may see the difference with ‘flex-direction: column;’ property. http://codepen.io/anon/pen/zroYEP?editors=110
If you’re setting “flex-basis: 0” the content shouldn’t be interesting, flexbox is just using the flex-grow or flex-shrink properties to calculate the width as you wanted in your first example.
So try the following code in the second example with more content in the aside and the result should be the same as in example1:
here is the example:
Yes, of course. It’s basically the same calculation as in the explanation of the first example (in “Alright, but why does the first demo work?“), but with setting the width explicitly . Thanks for pointing that out.
As it can be seen in your demo, you could actually just write
flex: 2
andflex: 1
, because the shorthand setsflex-basis
to zero.No, we’re talking about the 2nd example where you have the content inside. You need to set the flex-basis to 0px, if not it is set to auto and the aside is taking up way more width as you can see in your 2nd example and as you explained in the article.
We are not calculating anything here or setting any width at all. Just using the flex-grow and flex-shrink properties ratio (in the same way asyou did in example1, and tried in example2 just by setting flex-basis: 0px and the content doesn’t matter)
Yes, I understand, we’re talking about the same thing here. ;) I have provided the explanation and you a possible solution. The calculation explains my first example as well as your example with
flex-basis: 0;
.By the way, as we’re working on the x-axis in this example, we are actually setting the (initial) width with
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.
https://drafts.csswg.org/css-flexbox/#valdef-flex-flex-basis
Awesome post man! We use flexbox for some big apps right now and this post gave me some nice insights cause I had the same problem with flex-grow but wasn’t sure how exactly it works up until now.
Thanks! Pleased to hear that.
Thank you! It takes me a lot of time to realize all these things too.
In practice, I also see that there are two cases I used a lot:
Thank you for sharing!
Great post. Thanks Manuel!
Thank you, Pawel!
Awesome! Explained really well. That’s been a big help, thanks.
Thank you, glad to hear that!
Lovely article. Flex grow, shrink and basis can really trip you if you’re not careful. The key to understanding flexbox is understand axes, lines and how flex items are quite accommodating and try to use up free white-space intelligently. Flexbox doesn’t always work how you expect it, but it’s often the axes or the white-space that’s the cause of the confusion. Over a year into using flexbox, and I’m still learning. Articles like yours are super helpful :)
Thank you! We’re all in the same boat here. Flexbox offers so many new properties and possible ways of combining them, that at some point it can be quite intimidating. We just have to keep using these new properties and learning about them. ;)
Absolutely brilliant explanation!
I have been using flexbox a lot recently and the second I saw your space division diagram I finally ‘got’ flex-grow.
Understanding this completely lets you use it with much more confidence in more situations
Thank you for the nice feedback. I’m glad my article helped!