The elements on the page are organised in the same way as words in a book: from left to right and from top to bottom.
If you have been using CSS for a while, you will have a good understanding of block
and inline
elements. You know how they work. But you may occasionally get baffled when you forget that some properties exist only on block
elements and some on inline
elements.
Before we dive into the flex-box
model, leaving behind the memories of block
and inline
elements, let's quickly sum up what we know about them.
width
and height
properties,There is one more difference between block
and inline
elements.
An inline
element doesn't have width
and height
properties.
Try and change the dimensions of the span
element below to: width: 100px;
and height: 100px;
.
What happened?
Nothing.
Change the span
's display property to block
, by adding display: block
.
Now the span
proudly presents its content as a block
element. And block
elements have both — width
and height
.
What if you wanted to change the dimensions of an inline
element but didn't want it to move to the next line?
You could assign an inline-block
value to the display
property.
When an element becomes an inline-block
element, it still behaves like an inline
element — it doesn't jump to the next line — but you can change its dimensions. Let's try it.
Change the display
property of the span
element to display: inline-block;
below:
width
and height
properties.width
and height
properties.width
and height
.What happens to an element when you change its display
property to flex
?
A new relation between the parent container and its children is established.
The parent container becomes a flex container. Its children become flex items. And it doesn't matter if they were block
or inline
elements in their previous life.
To illustrate this, let's create a layout that you wouldn't use in real-life. For visualisation purposes only.
Below, I placed two block
elements, div
and p
, and an inline
element, span
, inside the inline
element em
. I hear you — complete mess.
Now, add display: flex
to the container
in CSS tab.
You've just created a new context and a new relationship between the parent element, the container
, and the children elements with a class item
.
The container
has became a flex container. Its children have become flex items.
The flex container behaves like a block
element. It takes up all available space. You can also change its width
and height
.
What about the flex items?
The flex items behave almost like inline-block
elements. They don't start at the new line. They take only as much horizontal space as they need. You can set their width
and height
.
But they are like inline-block
elements with superpowers.
They stretch to take up all vertical space.
Add height: 200px;
to the container
and see how the flex items grow vertically when the container's height changes.
The reason for that is that one of the container's properties, align-items
is set to stretch
by default.
After setting the container's display
property to flex
, its children (flex items) get three superpowers — flex-grow
, flex-shrink
, and flex-basis
. These properties let you decide how much space flex items will take.
By default they are set to:
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
The flex-basis
is the equivalent of the width
property of an inline-block
element.
By default, it's set to auto
.
When it's set to auto
, it sets the width
of the flex item to the value of its width
property. If the width
property was not set, the element will take up only the necessary horizontal space to display its content. Like an inline-block
element.
In the example below, add width: 100px;
to the item
class:
Now, all the items have the width of 100px
.
But in the flex box model, it is a good practice to use flex-basis
instead of width
to set the element's width.
When you set the value of flex-basis
to anything other than auto
, the width
property will be ignored.
Let's try it. Below the width
of flex items is set to 100px
. Add the flex-basis: 50px;
to the item
class:
Now, the width
property is ignored and flex items take only 50px
.
The flex-basis
takes any CSS units. Try it, by setting the flex-basis
to 25%
above.
Now, each flex item takes 25% of its parent.
It's very important to understand how the 25% is calculated.
Let's set the width
of the flex container to 400px
and the flex items' flex-basis
to 25%
:
To understand how the width of flex items is calculated, start with the inner width of the flex container (parent element):
If the flex container's width is 400px
, its padding is set to 10px
, and the box-sizing
is not set to border-box
, the inner width equals 400px
If the flex container's box-sizing
is set to border-box
, its inner width is 400px
width - 2 x 10px
padding = 380px
Now , you can calculate the flex item's width. Let's take the first example — when the flex container's inner width is 400px
:
If the flex item's box-sizing
is not set to border-box
, its width equals 400px * 25% = 100px
. And its total width equals 100px
+ 2 x 10px
padding = 120px
If the flex item's box-sizing
is set to border-box
, its total width equals 100px
, and its inner width is 100px
width - 2 x 10px
padding = 80px
Let me show you an example. You may open the Developers Tools and inspect how the size of the flex container and flex items changes. It really helps.
Below, the flex container's width
is set to 400px
. It also has padding
set to 10px
. Because I didn't set the box-sizing
to border-box
, its inner width is 400px
. And this value will be used to calculate the flex items width:
25% * 400px = 100px
Now, set the flex container's box-sizing
to border-box
. Its total width will be 400px
and its inner width will drop to 400px
- 2 x 10px
padding = 380px
.
That's why the flex items shrank from 100px
to 25%
* 380px
= 95px
.
Remove the box-sizing
line from the flex container and add padding: 10px;
to the item
class. The flex items grew. Each has now the total width of 120px
. The inner width is still 100px
or 25%
of the flex container.
Notice that visually it doesn't make sense — three containers, each 25%
wide take more than 3/4 of the flex container's width.
I hope that now you understand why there is nothing wrong with the flex box model. This is how the flex item's width is calculated.
What will happen when you add the box-sizing: border-box;
to the item
class?
The total width (inner width + padding) of the flex item shrinks from 120px
to 100px
. That's why flex items will take less space.
Let's do one more exercise — add the margin: 5px;
to the item
class. Nothing has changed. The flex item's width is still 100px
(inner width + padding).
Finally, remove the box-sizing
line from the item
class. Now, the flex item's total width increased again to 120px
:
100px + 2 x 10px padding = 120px
Until now, the maximum width of 3 flex items was 3 x 120px = 360px
plus margins. The minimum width of the flex container was 380px
. Flex items nicely fit into the flex container.
But what happens if we increase the width of flex items by changing their flex-basis
property and set it to 200px
? They should overflow the parent container. But — magically — they don't.
This is where the second superpower — flex-shrink
— kicks in. By default, all flex items have the flex-shrink
property set to 1
. It means that flex items will shrink proportionally to fit in the flex container.
Let's create three flex items with the width of 200px
. The total width of flex items is now 600px
.
Since the flex container's width is only 400px
, to fit in, flex items have to shrink. The excess 200px
have to be shaved off. Because all the flex items have a flex-shrink
set to 1
by default, they will shrink proportionaly by:
200px / 3 = 66.66px
So, their width will shrink to:
200px - 66.66px = 133.33px
You can inspect the flex items' width
in the Developer Tools to see the exact width.
The flex-shrink
property can take also higher values than 1
. Let's set the flex-shrink
of the .second
flex-item to 2
.
Now, the excess of 200px
will allocated proportionally to the flex-shrink
value of a flex item.
In the CSS below I added flex-shrink: 2;
to the .second
class.
The second component shrank more than the first and the second one. The sum of flex-shrink
values of 3 flex-items is 1 + 2 + 1 = 4
.
That's why the excess 200px;
is divided by 4
.
The first component's width will be: 200px - 1 x 50px = 150px
, the second 200px - 2 x 50px = 100px
and the third one 200px - 1 x 50px = 150px
.
You can play around with a flex-shrink
by assigning different values to see how nicely flex-items shrink to fit in into the flex container.
Remember: a flex-shrink
value tells you at what proportion the excess width
is deducted from the original width of the flex items.
But there is one more important point — flex-shrink
shaves off the excess width proportionally to the flex-shrink
value, but also to the original width of the flex item.
Until now, flex items had the same width. Let's see what happens if flex items have different width and different flex-shrink
values assigned.
Below, the first and the third component have the width
of 200px
and the flex-shrink
of 2
. The second component is only 100px
wide and its flex-shrink
is set to 1
.
The total width of all flex items is 2 x 200px + 100px = 500px;
. It means that the excess width is 500px - 400px (flex container) = 100px
and it needs to be trimmed off proportionally to the flex-shrink
value, and to the flex items original width.
Let's find out what are the calculations the flex model does to find out by how much to shrink flex items.
At first, it divides the width
of each flex item by the width
of the smallest flex item. The smallest flex item is 100px
wide. So, for the first flex item, the ratio will be 200px / 100px = 2
. For the second flex item, it equals 100px / 100px = 1
. The third flex item will have a ratio of 2
because it has the same width
as the first one.
In the next step, the flex-box multiplies the ratio by the value of flex-shrink
. In our example:
first flex item: 2 x 2 = 4
second flex item: 1 x 1 = 1
third flex item: 2 x 2 = 4
The sum of above numbers is 4 + 1 + 4 = 9
.
Next, flex box calculates by how much it should shrink each flex item. Assuming that the overflowing width of the three flex items is 100px
:
first flex item: 4/9 * 100px = 44.44px
second flex item: 1/9 * 100px = 11.12px
third flex item: 4/9 * 100px = 44.44px
Finally, the results are deducted from the original width of each flex item. This is how we get the final widths:
first flex item: 200px - 44.44px = 155.56px
second flex item: 100px - 11.12px = 88.88px
third flex item: 200px - 44.44px = 155.56px
Let's do a quick check: 155.56 + 88.88 + 155.56 = 400px
. Perfect fit!
When you change the display
property of an element to flex
, its children become flex items with the flex-grow
set to 0
.
They take only as much space as it is necessary to display their content.
The flex-grow
property gives you another superpower. It allows you to increase the width
of flex items so they nicely expand and take all available space of their parent — the flex container.
In the example below, I set the flex-grow
to 1
in the item
class. All components expanded to absorb all available space.
The available space is the difference between the inner width of the flex container and the sum of widths of the flex items' content.
Here, I have placed additional span
elements so we can exactly measure the content's width in Developers Tools:
The widths of the flex items' content are: 56.44px
, 10.67px
and 21.31px
respectively. That sums up to 88.42px
.
The remaining available space 400px - 88.42px = 311.58px
will be divided by 3
and allocated to flex items (3 flex items have flex-grow
set to 1
). That's why each flex item will grow by 103.85px
.
And so you get the following widths:
first flex item: 160.29px
second flex item: 114.52px
third flex item: 125.16px
That's 399.99px
. The tiny error comes from the rounding.
You can use the flex-grow
property to figure out proportion at which the flex items will absorb the available space.
Let's set the flex-grow
of the second item to 3
:
It may surprise you, that the second flex item didn't get three times wider than its siblings.
The available space will be divided by the sum of flex-grow
values of all three flex items.
1 + 3 + 1 = 5
And the flex items will grow by:
first flex item: 1/5 * 311.58px = 62.32px;
first flex item: 3/5 * 311.58px = 186.95px;
first flex item: 1/5 * 311.58px = 62.32px;
Final widths of flex items:
first flex item: 56.44px + 62.32px = 118.76px;
first flex item: 10.67px + 186.95px = 197.62px;
first flex item: 21.31px + 62.32px = 83.63px;
Sometimes, you will want to have more control over the flex items' width. Sometimes, you will want them to grow but not beyond a certain value. And sometimes, you will want them to shrink, but not below a specific width.
As soon as you start using the flex-basis
to define the width of a component, the width
property is ignored. The min-width
and max-width
properties set the width boundaries of an element.
In the example below, I set the min-width
of all flex items to 100px
. Although they have the flex-basis
set to 50px
, the flex-basis
will be ignored. And the flex items will be 100px
wide.
Now, if you resize the window, the sidebar width will remain in the range of 100-300px
. When min-width
and/or max-width
are set, the flex-basis
property works only in the range between these two values.
The min-width
and max-width
are extremely useful when used with percentages to set the flex-basis
.
In the example below, I set the sidebar
and the main-area
to 30%
and 70%
respectively.
Additionally, I set the sidebar min-width
and max-width
to 100px
and 300px
respectively, to make sure that its width remains in the range of 100-300px
.
Resize the window to see, how nicely the sidebar's width will adjust. But at the same time it stays in the specified range.
Rules to remember when using flex-basis
, min-width
, and max-width
:
flex-basis
is set to auto
(default) and a width
of the flex item is not set, the flex item takes up only as much the space as it is necessary to display its contentflex-basis
is set to auto
and you set the width
, the flex item will use the width
property to determine its widthflex-basis
of the flex item to anything other than auto
, the width
property will be ignored and the width set by the flex-basis
will be usedflex-basis
is taken into account when it falls between the min-width
and the max-width
flex-basis
is set to a lower value than the min-width
, an element will have a min-width
flex-basis
is set to a value that is higher than the max-width
, the element will have a max-width
You can use the 3 superpowers of flex items separately, like in the previous examples, or you can define them in one line using the flex
property following the pattern below:
flex: flex-grow-value flex-shrink-value flex-basis-value;
For example, instead of:
flex-grow: 1;
flex-shrink: 1;
flex-basis: 25%;
you can use the abbreviated syntax:
flex: 1 1 25%;
The flex
property can be assigned several predefined values. So you can use them as long as you understand what will happen to your flex items:
1. flex: initial;
— equivalent of flex: 0 1 auto;
. This is the default value assigned to flex items, when you set display: flex
on their parent element.
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
A flex item is sized according to its width
and height
properties (because of flex-basis: auto;
). It shrinks to fit into a flex container but doesn't grow to take up all available space.
flex: auto;
— equivalent of flex: 1 1 auto;
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;
A flex item is sized according to its width
and height
properties. It grows to take up all available space and it shrinks to fit into a flex container
flex: none
- equivalent of flex: 0 0 auto
flex-grow: 0;
flex-shrink: 0;
flex-basis: auto;
The element behaves like an inline element. You can set its width
and height
and it will not grow or shrink. If the width
and height
are not set, it takes up only as much width as it is necessary to display its content.
It is crucial to understand underlying calculations of the three flex-box model superpowers to be able to figure out how the flex-box model will behave. It will save you time and frustration in the future.
Some calculations are counterintuitive. For example, when you set the flex-basis
to 25%
, the three components took much more space than expected. Now you
understand why.
Here are the main takeaways from the article:
flex-basis
instead of the width
property to control the width of a flex item. Once you set the flex-basis
to anything other than auto
, the width
property is ignored.min-width
and/or max-width
of the flex items, the width set by flex-basis
will be taken into account only within the min-width - max-width
rangeflex-shrink
valueflex-grow
value