CSS Flexbox Layout Deep Dive
CSS Flexbox Layout (spec) has taken over the online world since its introduction. However, due to its intuitive character, many use it without properly learning its deep secrets. As a result, we have seen the same visual artefacts appear again and again, typically when there is "not enough room to put things", were shrinking is done. A great example is the one of a mobile app developed on an iPhone 13 which is then opened by an end user on an iPhone 7 and looks like shit.
We will not go into the greatest details of CSS Flexbox Layout but we will get a deep understanding of the basic concepts of growing and shrinking, which are by far used most often. I hope you're smart, because it's hard ;).
The "A Complete Guide to Flexbox"
Almost every common Google-search related to CSS Flexbox Layout links to "A Complete Guide to Flexbox" on the first page. Read this guide. Unlike the expectations created by its title, it is by no means a complete guide. However, it should get you started exploring CSS FLexbox Layout.
You now know that there as soon as you put display: flex on an element, that element becomes a flex container. You also know that its direct descendants are now flex items on which you can set the flex: <flex-grow> <flex-shrink> <flex-basis> property. Furthermore, you know that putting this flex-property on a flex container (that is not a flex item) makes no sense at all.
If you don't know these things, reread the guide. The rest of this text assumes that you have read it thoroughly.
The flex Shorthand Property: "Intelligent" Defaults you should NEVER Use.
Let us focus on the single most important property of CSS Flexbox Layout: the flex shorthand property for flex items. Oh Lord, this one is one big pita (the pain, not the highly nutritious meal).
The flex shorthand property controls three flex-related properties: flex-grow, flex-shrink and flex-basis of flex item's. Roughly, together, these define a flex item's "flexibility".
flex-grow: if there is free space, how much should we give to this flex item?flex-shrink: if there is insufficient space, how much can we take from this flex item?flex-basis: what is the size of the flex item would we not apply CSS Flexbox Layout's magic of growing and shrinking?
First of all, our number one rule: it is a good practice to always add a flex shorthand property to every flex item. You might think that this is unnecessary ("it looks great on my device") and that I'm being a bitch for no reason, but if you assume that the default value for flex is the correct one, it is the end user that will be the one discovering your assumption. Let me explain why it is not that simple, but first, let's forever engrave our rule in our minds.
RULE: As soon as the Good Developer adds
display: flexto an element, he adds aflexproperty to every new flex item (this is, every direct child of that element).
So, basically, the flex shorthand property can be really really weird. The guide we read says "The shorthand sets the other values intelligently"; well this intelligence, although ment to reduce typing, which in practical situations it will, can really blow you away. Let's look at some examples.
| value | calculated equivalent | what happened? |
|---|---|---|
| (not provided) | flex: 0 1 auto; |
- |
flex: 2; |
flex: 2 1 0%; |
Changing flex-grow changed flex-basis to from auto to 0%. |
flex: 2 2; |
flex: 2 2 0%; |
Changing flex-grow & flex-shrink changed flex-basis to from auto to 0%. |
flex: 10px; |
flex: 1 1 10px; |
Changing flex-basis changed flex-grow from 0 to 1. |
flex: none; |
flex: 0 0 auto; |
Special keyword that sets everything. |
flex: initial; |
flex: 0 1 auto; |
Special keyword that sets everything. |
flex: auto; |
flex: 1 1 auto; |
Special keyword that sets everything. |
Now visit this website for a full explanation. Make sure to repeat it enough times so that you never forget it.
Although it is not that hard to master this odd highly-intelligent behavior (it just took you some memorisation) your team or any future developer will probably not have the same knowledge. As a result, it is a good idea to always specify the flex-grow, flex-shrink and flex-basis parameters explicitly, even though the latter two are optional. Let's make this a rule.
RULE: When the Good Developer uses the
flexproperty, he always specifies theflex-grow,flex-shrinkandflex-basisparameters explicitly.
Example Exercise
Someone wrote the following CSS snippet, which is not according to Codifly's rules but which works as intended. Correct it like you will do time and time again when you see similar code in one of your projects.
.flex-container {
display: flex;
}
.flex-item-1 {
}
.flex-item-2 {
flex: auto;
}
.flex-item-3 {
flex: 1;
}
Solution:
.flex-container {
display: flex;
}
.flex-item-1 {
flex: 0 1 auto;
}
.flex-item-2 {
flex: 1 1 auto;
}
.flex-item-3 {
flex: 1 1 0%;
}
Intermezzo: min-content and max-content
The width property can take a specific value like 10px or 5%, but two keywords can be used to base the width of an element on its content:
min-content: what if the width if soft wrapping is applied everywhere where soft-wrapping is available? In other words, what is the length of the longest word in the element?max-content: what if the width if soft wrapping is NEVER applied, this is: how long is the entire text in the element?
Read about min-content and max-content on MDN.
The Flex Algorithm: a Mental Model that (Often) Works
The algorithm is given in all its glory in the spec but let's simplify it to something we can use in our everyday dev life. We will take a few assumptions, but at least we will have something instead of just "gut feeling".
- Calculate the available space within the flex container. This is the size of the container.
- For each flex item in the flex container, calculate the "base size". The base size is the minimum size a flex item needs, calculated via the principle of
min-content. - For each flex item in the flex container, calculate the hypothetical main size". The hypothetical main size is the size of an element, before CSS Flexbox Layout does its magic. It is calculated as follows:
- If
flex-basisis notauto: use the value offlex-basis. This includes the value0! - If the element has a "definite width": use the
width. - Otherwise, determine it based on the content via the principle of
max-content. Remembermax-content?
- If
- Subtract the sum of the hypothetical main size of all flex items from the available space within the flex container, this is the "free space", which can be either positive or negative.
- It now depends:
- In case of positive free space distribute the free space using the grow factor
flex-grow. The size by which each element grows isgrow_factor / sum(grow_factors). - In case of negative free space fix the lack of free space using the shrink factor
flex-shrink*base size(notice how shrinking behaves differently from growing!). The size by which each element shrinks isshrink_factor / sum(shrink_factors).
- In case of positive free space distribute the free space using the grow factor
Note that growing behaves differently from shrinking! When growing, the flex-grow value is used a growth factor. However, when shrinking, the shrink factor is flex-shrink times the base size!
Note: The flex shrink factor is multiplied by the flex base size when distributing negative space. This distributes negative space in proportion to how much the item is able to shrink, so that e.g. a small item won't shrink to zero before a larger item has been noticeably reduced.
Example Exercise 1
Let us look at a simple example together. Use the flex algorithm to calculate the effect of the following CSS snippet.
.flex-container {
display: flex;
width: 500px;
}
.flex-item-1 {
flex: 3 1 auto;
width: 200px;
}
.flex-item-2 {
flex: 1 1 auto;
width: 100px;
}
First, calculate the available space of the flex container and the hypothetical main sizes of the flex items. This corresponds to the algorithm's step 1, 2 and 3.
| Item 1 | Item 2 | Total | |
|---|---|---|---|
| Available space | 500px | ||
| Hypothetical main size | 200px | 100px | 300px |
Next, since the sum of the hypothetical main sizes is lower than the available space, we write down the flex-grow values (otherwise, we would have written down the flex-shrink values), from which we can distill our grow ratio's. Finally, we distribute the positive free space. This corresponds to the algorithm's step 4 and step 5.
| Item 1 | Item 2 | Total | |
|---|---|---|---|
| Available space | 500px | ||
| Hypothetical main size | 200px | 100px | 300px |
| flex-grow | 3 | 1 | 4 |
| Grow ratio (%) | 75% | 25% | 100% |
| Free space distr. | 150px | 50px | 200px |
| Result | 350px | 150px |
Example Exercise 2
Use the flex algorithm to calculate the effect of the following CSS snippet.
.flex-container {
display: flex;
width: 800px;
}
.flex-item-1 {
flex: 0 4 300px;
}
.flex-item-2 {
flex: 0 6 600px;
}
This scenario is very similary, however, in a flex-shrink case, the calculation of the shrink ratio is a little bifferent: as you have seen, it is based both on the value of flex-shrink and on the base size of the elements. Result:
| Item 1 | Item 2 | Total | |
|---|---|---|---|
| Available space | 800px | ||
| Hypothetical main size | 300px | 600px | 900px |
| flex-shrink | 4 | 6 | 10 |
| Shrink ratio (%) | 25% | 75% | 100% |
| Free space distr. | -25px | -75px | -100px |
| Result | 275px | 525px |
Common pitfalls
Beware of min-width and max-width
In the main algorithm have seen that flex-basis is used before the width is considered. However, in contrast, min-width or max-width have a rather "high preference" and behave like you would intuitively expect: these values limit the size of an element even after CSS Flexbox Layout has done its job. An element will never grow larger than its max-width or shrink smaller than its min-width.
Flex Items Can Not Be Inline Elements
Flex items can never be inline elements. As a result, it makes no sense to add display: inline to any flex item. Neither does it make sense to set display to inline-block, inline-flex, inline-grid or inline-table. Don't do it.
In case you are wondering what happens if you do: the browser will crash. Ok, it won't, but the value for display will be converted to the non-inline equivalent. Anyways, you're a Good Developer, so this will not happen in your applications.
RULE: The Good Developer knows that flex items can never be inline elements.
Using space-between for Gaps Stinks
In the past years, we have seen a phenomenon occuring again and again. It works like this: several items have to be shown next to eachother, with a gap in between them. It seems tempting to use space-between to create the gap. Let us consider the most simple example:
.flex-container {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.flex-item {
flex: 0 0 21%;
}
With four children this looks correct. You might now think: I did a good job!

Unfortunately, this approach is very fragile: as soon as other children are added (and this always happens), we get the following phenomenon:

If you don't see something wrong here, suppose that Finder's layout would look like this. This is clearly not wanted.

A more modern solution is to use the column-gap property instead of the space-between property and you are good to go! Before moving on, can you see how we calculated the percentage?
.flex-container {
display: flex;
- justify-content: space-between;
flex-wrap: wrap;
+ column-gap: 5.3333%;
}
.flex-item {
flex: 0 0 21%;
}
RULE: The Good Developer does not use
space-betweento create columns with gaps between them. He usescolumn-gapinstead.
Exercises
PLEASE SUBMIT ANSWERS AND PROVIDE FEEDBACK USING THE FOLLOWING FORM: https://forms.gle/QNKFkDwpe4PftavN9.
Exercise 1
A bright mind wrote the following CSS snippet, which is not according to Codifly's rules but which works as intended. Correct it like you will do time and time again when you see similar code in one of your projects.
.flex-container {
display: flex;
}
.flex-item-3 {
display: inline;
flex: 10px;
}
.flex-item-4 {
flex: 1 0;
}
.flex-item-5 {
flex: auto;
}
Exercise 2
Use the flex algorithm to calculate the effect of the following CSS snippet.
.flex-container {
display: flex;
width: 500px;
}
.flex-item-1 {
flex: 2 1 100px;
width: 200px;
}
.flex-item-2 {
flex: 1 1 auto;
width: 100px;
}
Exercise 3
Use the flex algorithm to calculate the effect of the following CSS snippet.
.flex-container {
display: flex;
width: 500px;
}
.flex-item-1 {
flex: 3 1 68px;
}
.flex-item-2 {
flex: 1 1 auto;
/* note: contains 32px of text */
}