How Flexbox Works
Unlike many parts of CSS, Flexible box layout or Flexbox can be difficult to understand just by using it. It wasn't until this most recent update of CSS Master that I even sort of grokked it.1 I hope to untangle its mysteries in this post.
Flexbox was designed to distribute elements and space within a containing element. Unlike CSS Grid which aligns objects vertically and/or horizontally, Flexbox works in a single direction: a row or a column.
To distribute space, first the browser has to determine how much space is available. Browsers use the following process, more or less.2
- Calculate the available space within the flex container. That's the main size of the container minus the size of its margins, padding, borders, and gaps.
- Calculate the flex base size and hypothetical main size of each flex item, using the value of
flex-basis
,min-width
, or the size of the flex item's contents — whichever is greater. Flex base size is the minimum size that a flex item requires. Hypothetical main size refers to the size of a flex item before the flex factors are applied. The flex base size of a flex item will never be smaller than that of its contents. - Calculate the total hypothetical main size for all flex items.
- Compare the total hypothetical main size of all flex items to the available space within the flex container. When total size exceeds available space, use the value of
flex-shrink
as the flex used factor. When total size is less than the available space, use the value offlex-grow
as the flex used factor. Browsers use either the value offlex-shrink
orflex-grow
for each line of flex items.
The flex used factor determines what proportion of the leftover space to add or subtract from each flex item. Browsers complete the calculation for each flex item, in a loop.
Leftover space is what remains after subtracting the sum of items with a definite size, plus padding, and gap values from the interior width of the flex container. Think of definite size as the known size
of a flex item. When a flex item has a definite size, it's considered an inflexible item. Items that do not have a definite size are considered flexible items. Only flexible items have their size adjusted by the flexible box layout algorithm. This space adjustment is called flexibility.
I think Firefox’ Flexbox inspector currently does the best job of explaining how Flexbox works. Chrome and Edge indicate whether a flex item has grown or shrunk, however, Firefox also tells you by how much. I recommend using it to inspect the examples in this post.
In all of the following examples, the flex container is 1200 pixels wide.
A simple Flexbox example
Figure 1 uses the initial property value for flex
— 0 1 auto
. The flex
property is a short hand for the flex-grow
, flex-shrink
and flex-basis
properties. Here:
- the value of
flex-grow
is zero; - the value of
flex-shrink
is 1; and - the value of
flex-basis
isauto
.
Because the value of flex-basis
is auto
, the browser uses the max-content
value as the base size of each flex item. The total hypothetical main size of these flex items is less than the amount of space available within the flex container. As a result, these flex items neither grow to fill the available space, nor shrink to fit within it.
Before continuing, let's take a detour into the syntaxes of the flex
property.
A detour into the syntaxes of the flex
property
flex
accepts at least one value, and as many as three.
When you use one-value syntax, the value gets interpreted as flex: <number> 1 0
, where <number>
is the flex-grow
value. In other words, flex: 2
is equivalent to flex: 2 1 0
.
When you use two-value syntax, the first value is interpreted as the flex-grow
value. However:
- If the second value is a number, it's interpreted as the
flex-shrink
value - If it is a valid value for the
width
property, it's interpreted as theflex-basis
value.
In other words, flex: 1 0
is the same as writing flex: 1 0 0
, but flex: 1 30rem
is equivalent to flex: 1 1 30rem
.
When you use three-value syntax:
- The first value is the
flex-grow
value. - The second value must be a number. It's the
flex-shrink
value. - The third value must be a valid value for the
width
property. It's interpreted as theflex-basis
value.
Think of flex-basis
as a starting size. When a flex item has been set to grow, browsers add flexibility to the flex-basis
value. When it's been set to shrink, browsers subtract flexibility from this value.
When little bitty flex items grow up
Figure 2 shows what happens when you add flex: 1
to every flex item (the equivalent of flex: 1 1 0)
.
Here the total hypothetical main size of our items is less than the interior space of the container. As a result, the browser uses flex-grow
as the flex used factor. Every flex item in this example has a flex-grow
value of 1
, and a flex-basis
value of zero.
Browsers loop through each flex item to determine its flexibility using the following calculation:
flexibility = (free space ÷ sum of the reamining flex items' flex-grow
value) × flex-grow
Adding or subtracting the flexibility amount to or from the value of flex-basis
gives us the element's final size. Let's calculate the flexibility of item A. Remember that our container is 1200 pixels wide.
( 1200 ÷ ( 1 + 1 + 1 + 1 + 1 ) ) × 1 = 240
Item A has a flexibility of 240 pixels. Next, add this flexibility value to its flex-basis
to determine its size: 0 + 240 = 240.
According to this math, item A should be 240 pixels. However, Antidisestablishmentarianism is a long, unhyphenated word that requires 417 pixels of space3. Instead of setting the size of A to 240 pixels, the browser clamps its width to its minimum content size of 417 pixels. Now there are 783 pixels of space to distribute across items B, C, D, and E.
For item B, the calculation looks like this (remember that 0 is the value of flex-basis
for every flex item:
( 783 ÷ ( 1 + 1 + 1 + 1 ) ) × 1 = 195.75 0 + 195.75 = 195.75
Subtract the size of item B from the remaining amount of space: 783 - 195.75 = 587.25. That leaves roughly 587 pixels to allocate to items C, D, and E.
C: 0 + ( 587 ÷ ( 1 + 1 + 1 ) ) × 1 ) = 195.67 D: 0 + ( 391.33 ÷ ( 1 + 1 ) ) × 1 = 195.665 E: 0 + ( 391 - 195.665 ÷ 1 ) × 1 = 195.335
The browser deducts the allocated space from the remaining space then calculates the flexibility and size of the next flex item.
How much space the contents of a flex item require depends on:
- the value of the
font-size
property; - the height and width of the font's glyphs;
- the length of particular words and lines of text; and
- the intrinsic or specified size of elements, such as images and video, contained by the flex item.
Variations in how browsers and operating systems render text also play a role.
flex-grow
and proportion
In Figure 3, item C has a flex
value of 5
(flex: 5
).
Its siblings have a flex: 1
declaration. Here's the CSS.
div > :not([id=c]) {
flex: 1;
}
[id=c] {
flex: 5;
}
Remember that flex: 1
is the equivalent of flex: 1 1 0
and flex: 5
is the same as flex: 5 1 0
. Every flex item in this example is set to grow from a flex-basis
of zero. However, the flex-grow
value of 5 means that item C should receive roughly five times as much flexibility as its siblings.
Here too, Antidisestablishmentarianism forces item A to have an inline size of 417 pixels. That leaves 783 pixels to allocate proportionally across items B, C, D, and E.
Let's calculate the size of item B. There are four remaining flexible items, and the flex-grow
value of C is 5. That means we'll divide 783 by 8, which is the sum of each flexible item's flex-grow
value. We'll then add that to our flex base size of 0.
0 + ( 783 ÷ ( 1 + 1 + 5 + 1 ) ) × 1 = 98
Item B is roughly 98 pixels wide. Subtracting 98 from 783 leaves about 685 pixels of space reamining in the container. Now let's calculate the size of items C, D, and E.
C: 0 + ( 685 ÷ ( 5 + 1 + 1 ) ) × 5 = 490 D: 0 + ( 195 ÷ ( 1 + 1 ) ) × 1 = 97.5 E: 0 + ( 97.5 ÷ 1 ) × 1 = 97.5
Since item C has a flex-grow
factor of 5, it receives five times as much flexibility as items B, D, and E.
When flex-basis
is auto
In Figure 4, the item C still has a flex-grow
value of 5. However, for each sibling element, the used value of flex
is its initial value: 0 1 auto
.
In the previous example, items A, B, D, and E have a flex-grow
value of 0
. Because their flex-basis
value is auto
, their inline size will be as wide as their content demands. For item A, that's the size of Antidisestablishmentarianism and its bold-letter A. For items B, D, and E, it's the size of each letter.
Items A, B, D, and E all have a definite size. Right away, we can determine how much space remains in the container:
1200 - ( 417 + 33 + 35 + 30 ) = 685
Since item C is the only flexible item in the container, it grows to fill the remaining space.
0 + 685 = 685
Again, I've rounded some of these numbers. Your browser's Flexbox inspector may report slightly different values.
When flex items shrink
Let's look at an example of a Flexbox layout when flex-shrink
is the flex used factor. In Figure 5, every flex item has a flex-basis
value of 500 pixels. Items B, C, D, and E all have a flex-shrink
value of 1 and a flex-grow
value of 0. Item A has a flex-shrink
value of 5.
Here, the total hypothetical main size for all flex items is 2500 pixels — the sum of the flex items' flex-basis
values. Since the total hypothetical main size is larger than the size of our container, the browser uses the flex-shrink
factor.
Let's start by calculating the flexibility of item A. We'll start with the difference between the hypothetical size of our flex items and our flex container.
( 1300 ÷ ( 5 + 1 + 1 + 1 + 1 ) ) × 5 = 722.22
Next, we need to subtract this amount from the value of flex-basis
.
500 - 722.22 = -222.22
That's a negative value. If item A was an empty element, its main size would be clamped to zero pixels. Instead, the browser clamps item A to its minimum content size — about 34 pixels.
Since the size of item A is now known, we can subtract its size from the remaining space in the container: 1200 - 34 = 1166. Now we can calculate the size of each remaining flex item.
B: ( 1166 ÷ ( 1 + 1 + 1 + 1 ) ) × 1 = 291.5 C: ( 874.5 ÷ ( 1 + 1 + 1 ) ) × 1 = 291.5 D: ( 583 ÷ ( 1 + 1 ) ) × 1 = 291.5 E: ( 291.5 ÷ 1 ) × 1 = 291.5
Something to note: when flex-grow
and flex-shrink
both have a property value of zero, flex items neither grow nor shrink. Should the total hypothetical main size of all flex items exceeds that of the flex container, the flex items will stack along the main axis without wrapping. They may also overflow the container. You can change this behavior by setting the value of flex-wrap
to wrap
or wrap-reverse
.
Let's look at how browsers determine flexibility across multiple lines.
Flexbox and flex-wrap
In Figure 6, I've changed the value of flex-wrap
from its initial value of nowrap
to wrap
. The flex
property values are unchanged from Figure 5.
When the value of the flex-wrap
property is wrap
or wrap-reverse
, the browser uses each item's flex-basis
value to determine how many items can fit on a line. Flex items wrap at the point at which the sum of their hypothetical main size exceeds that of the container.
In Figure 6, flex-grow: 0
. As a result, there's 200 pixels of space remaining at the end of each of the first two lines. Changing the flex-grow
value to 1, on the other hand, creates the result shown in Figure 7. Each flex item grows to fill the remaining space in its line.
Setting flex-wrap
to wrap-reverse
works similarly. However, wrap-reverse
also reverses the flex items' visual rendering order.
Conclusion
Unlike other areas of CSS layout, Flexbox can be difficult to understand by using. Here's what to keep in mind.
-
Flexbox distributes space in a single direction — along a row or a column.
-
The
flex-basis
value determines the starting or base size of a flex item. However, the intrinsic size of a flex item's contents can force a flex item to be larger than the value offlex-basis
. -
For any line of flex items, the browser uses either the value of
flex-grow
orflex-shrink
— not both at once. -
Whether the browser uses
flex-grow
orflex-shrink
as the flex used factor depends on how much space is available along the main direction. -
Available space gets distributed across flex items proportionately, based on the value of each items used flex factor.
-
If we're being really honest, I'm not 100% sure I do. ↩
-
The specificaton includes a much more jargon-heavy, technical explanation of the Flexbox algorithm. ↩
-
How much space text requires depends on the length of non-hyphenated, non-breaking words, type size, and the dimensions of each glyph in the chosen font. In the associated demos, I've used IBM Plex Sans for consistency. ↩
-
Firefox, on some platforms or with some add-ons, may report a flexibility value of -772 pixels or something that seems a little bit wrong. The thing to know is this: whenever the hypothetical main size of a flex item is less than the size of its contents, its used size will be the minimum content size. ↩
Get CSS Master
Did you learn something from this blog post? You might like the latest edition of CSS Master. It contains several nuggets of CSS joy like this one. Buy now from SitePoint