Back to the index.
Written in March - April 2023.
This test page explains a simplified version of the flexbox algorithm that is enough to understand about 95% of practical use cases. No doubt there will be edge cases somewhere that break these rules, but I can't do everything.
I found the rules contained in this page by reverse engineering, and not by reading the spec. The spec is fairly unreadable as specs go, and besides it is confirmed that browser implementations do not follow the spec in one instance — and possibly in a second one, but I don't have confirmation on that.
Therefore this overview shows flexbox as it is actually implemented in browsers.
Here's the best explanation of the algorithm I found so far, by Tiffany Brown. It doesn't treat everything I want to know, though, so I rolled my own.
flex-basisis defined, all items get
widthis defined, all items get that width.
flex-basisis defined, all items get that flex-basis. Then, if
visible(or, in Firefox and Safari 15 or older only,
clip), items are allowed to grow to
min-contentif their contents overflow.
flex-basisare defined, all items get the
widthis ignored. However, if their content overflows they are allowed to stretch to the larger value of
flex-wrap: wrap[-reverse]is defined, the first flex item to overflow the flex container is wrapped to the next line. Repeat as often as necessary.
widthvalue. If there is no
width, items shrink to their
flex-growis defined, the remaining space is divided among the flex items based on their flex-grow value.
min/max-widthwork as expected, except that a combination of
flex-basiscauses browsers to follow the
widthmodel, and not the
overflowon the container works as expected: the flexbox uses the container width for layout, and if items overflow they're handled by the
aspect-ratioon items works as expected: the items attempt to take on the correct aspect ratio, but their height is constrained by the minimum height the content needs. If the content is too high the aspect ratio is not obeyed. This is in line with
aspect-ratiobeing a weak declaration.
Initially we'll study content that fits on one line. We'll get back to too-long content later.
width is defined all items get
width is defined, all flex items get that width. Their content may be wrapped if necessary, as with "Our service", or it may overflow the item, as with "Teamteam".
width is so narrow that all items overflow.
flex-basis is defined, all flex items get the larger of the defined
min-content. Thus, if necessary flex items stretch up beyond their defined
width to contain all of their content.
Home and Our Service fit in 25% of the container width (Our Service with text wrapping, but that's fine). Teamteam does not, and stretches up to
Same for 30%.
No text fits in 1% of the container width. Therefore all items stretch to
width. In the next example all items have
flex-basis: 25% and the second one has
width: 40%. The
width is ignored.
In the next example all items have
width: 25% and the second one has
flex-basis: 40%. The
width is accepted for the first and third item, but overruled by the
flex-basis for the second item.
The rules are somewhat more complicated than the two examples above suggest. If both
flex-basis are defined the following happens:
min-contentis smaller than the
flex-basis, the width is
min-contentis larger than
flex-basis, the width is the smaller of
min-contenton the one hand, and the larger of
flex-basison the other.
(I established these rules by reverse engineering. I have no clue if this is what the spec says should happen. But it's what's happening — at least in the examples below.)
Example 1: Home and Our Service are smaller than
flex-basis, so they get a 25% (
flex-basis) width. Teamteamm is larger, so it is allowed to stretch up to 30% (
width, which is larger than
flex-basis). That's still not enough, and the content overflows.
Example 2: We stretch up to
flex-basis still determines the width of Home and Our Service, but Teamteamm is now allowed to stretch up to 50%. It doesn't need that much to reach
min-content, though; about 37% is enough.
Example 3: Again Home and Our Service take
flex-basis (30%). Teamteamm is still too large, so it also
takes 30%, since
flex-basis is larger than
width here. The content still overflows.
Example 4: Now we use a measly
width: 1%. This yields the same result as the previous example for
the same reasons.
Example 5: Now we shrink to
flex-basis: 1%. None of the items have a
min-content smaller than the
flex-basis, so they all take
min-content. However, the upper bound is still 30%, as the larger of
flex-basis, so the Teamteamm still grows to 30% and overflows.
So far we used the default
overflow: visible for all examples. What happens if we change that?
width is easy. As you'd expect, the overflowing part of Teamteam is hidden when you set
However, it turns out that the
flex-basis behaviour changes: items no longer stretch to
min-content if necessary, but instead stick to the defined
width are defined,
overflow: [not-visible] also causes the flex items to take on their exact
flex-basis in all circumstances. Overflowing content is hidden.
overflow: hidden | auto | scroll cause the
min-content rule of
flex-basis to be suppressed.
overflow: clip also does so in most browsers, but in Firefox, as well as in Safari 15 and lower,
clip behaves the same as
visible and does not suppress the
This is possibly an old bit of behaviour that is being changed. My old Safari 12/Mac and 15/iOS do the same as Firefox, but my new Safari 16/Mac does not. Maybe a future Firefox will fall in line with the other browsers as well.
This example should not hide overflowing content.
The next two examples should both hide overflowing content, but the second one doesn't hide it in some browsers.
So far, the content fit on one line. Now we're going to look at what happens if it doesn't.
If the content is too long for one line, flexbox has two options: wrap the content to multiple lines, or shrink individual items. Wrapping is something you have to set explicitly with
flex-wrap: wrap[-reverse]. If you don't set it, shrinking takes place.
flex-wrap: wrap. After initial layout, any flex item that overflows the flex container is wrapped to the next line. This process is repeated if necessary.
This can be slightly confusing because wrapping takes place before shrinking. The example below is the same flexbox, once without and once with wrapping.
It might appear that wrapping is unnecessary, but that's not true.
Wrapping takes place at the time the Our Service item still has its initial width of
max-content. Picture it like this. (Here I faked stage 1 of the layout process; the box below is not a flexbox.)
tooLongText is pushed out of the flex container and is wrapped to the next line. Among other things that means that shrinking is unnecessary and Our service retains its
flex-basis rules we found earlier remain in force. In this
flex-basis example Teamteam is allowed to stretch up to
min-content, and as a result takes more than 25% of the width. That pushes the other items rightward, and the first wrap point is the second Home.
Therefore the wrapped version shows three items on each line.
Compare this to items with
width: 25%. Teamteam strictly takes one quarter of the container width and the text overflows its item if necessary. The first wrap point comes at the second Teamteam link.
Therefore the wrapped version shows four items on the first line and two on the second.
Play around with a few wrapping flex containers here.
If flex items overflow their container and wrapping is not allowed, the algorithm shrinks flex items.
The intended rule is that flex items are allowed to shrink to the smaller of
min-content and the defined
If we use
width this is exactly what happens. In this example most items have a
min-content smaller than 25%, and they shrink to
min-content. The two Teamteams have a
min-content larger than 25%, and stay at 25%. That's not enough, and the content still overflows, but the algorithm is satisfied.
In the next example Teamteam is allowed to take 30% of the width. As a result the total line is wider.
If we use
flex-basis only the algorithm still tries to find a defined CSS
width, doesn't find any, compares
undefined and goes with the
min-content. Therefore some flex items are wider in the
flex-basis example than in the
width example: they take a width of
(This is a confirmed bug in the flexbox implementation of all browsers. I do not expect this bug to ever be solved; by now there are plenty of flexbox layouts that depend on this behaviour.)
In these examples the value of
flex-basis doesn't matter. All items still take their
flex-basis are defined the algorithm finds a
width, uses it, and we get the same result as when only
width is defined. Defining
flex-basis is pointless here.
For the second example we'll take some content that is clearly too wide for its flex item: the image. In the
width model it stretches up its item to the allowed maximum of 30%. That's not enough, and the content overflows the item. The other items shrink just enough to allow the 30%-wide item to fit on the line. The two Home links don't have to shrink all the way to
min-content, shrinking to about 23% each is enough to fit the last item in the flex container.
flex-basis model the last item is stretched up to contain its content. Again the other items shrink, and now they need to shrink more than in the previous example, since the last item is now wider. Therefore the two Home items shrink to their
min-content as well. This is still not enough, and the final item overflows the flex container, but the algorithm has done its best.
width are defined
the official rule is followed: flex items are allowed to shrink to the smaller of
min-content and the defined
width (and not flex-basis). That's what happens here. The other flex items shrink equally to make room for the final item. Defining a
flex-basis is pointless.
Growing takes place if there's space left on a line and you defined
flex-grow. Obviously, either shrinking or growing takes place, not both. There's either too little or too much space left.
The rule is simple: we take the items' defined widths, calculate how much space is left, and divide that among the items relative to their
flex-grow value. Thus all items together will take 100% of the container width.
In the first example the items have
width: max-content, and they thus have a different width. About 49% of the container width is left.
If the items grow this 49% is divided equally among the two items, so each gets about 24%. Note that the items do not have the same width now. Teamteam starts out with a larger width than Home, and both gain 24%, so Teamteam remains larger than Home.
If one item gets flex-grow 1 and the other 3, the 51% is divided in a 1:3 ratio, so that the first gets about 13% and the second about 38% in addition to their base width.
When a width is defined, either through
flex-basis or through
width, that definition is used for the grow calculations. This matters for flex-basis. Take this example. As we saw earlier, in the
flex-basis model the second item becomes wider because it takes
min-content as its minimum.
However, the grow algorithm uses the defined
flex-basis of 25%, and not the actual width of about 30%. It then adds equal amounts of space, and as a result all items are equally wide.
width the same happens.
width. Thus, the next example is the same as the previous one. The
width: 40% on the second item is ignored.
In the next example the
flex-basis: 40% overrules the
width: 25%, and the second item has a larger base width and thus remains larger than the other two after growing.
Growing takes place after wrapping, and is done for each line individually. So the wrapping algorithm in stage 2 decides how the items are wrapped, and then the growing algorithm decides how items on individual lines are grown.
width, the wrapping algorithm divided the items over two lines 4-2. Thus, the items on line 1 are grown to 25% each, and those on line 2 to 50% each.
flex-basis, the wrapping algorithm divided the items over two lines 3-3. Thus, the items on both lines are grown to 33% each.
One final edge case: what if the content is too large even for the grown item? Here the second item grows to a
min-content of more than 33%.
If we grow the items, the left and right items grow as much as they can, but the middle one doesn't. It is effectively excluded from growth since it's already larger than 33%.
Compare this to
width, where the middle item overflows and doesn't take any extra space. After growing each item takes exactly 33%, and the middle one still overflows.
In a flexbox context
max-width mostly work as you'd expect: they constrain the effective width of flex items. Here flex items get
width: max-content, but at least 25%. The Home item is normally smaller than 25% but now takes that value. This is exactly what you'd expect.
Here flex items get
width: max-content, but at most 30%. The last two items are now capped at that width. This is exactly what you'd expect. Content may overflow.
Here flex items get
width: max-content, but at least 25% and at most 30%. The first one has a 25% width, the other two a 30% width. This is exactly what you'd expect. Content may overflow.
Here shrinking takes place. As you'd expect, shrinking stops at width 30%, so the first and last item remain wider than they otherwise would be.
flex-grow. If you use both items will grow, but not beyond
max-width. This is exactly what you'd expect. Content may overflow.
There's one exception where
min-width does not behave as you'd expect.
If an item has a
flex-basis and a
flex-basis is treated as if it is
width: the item is not allowed to stretch to its
You can see that below by adding
min-width: 25% to these examples.
The Teamteam item stretches up to its
min-content, as we've seen many times already ... until you add a
min-width to the mix. Then the
flex-basis definition is adhered to rigidly, and the Teamteam items no longer stretch to their
min-width overrules flex item shrinking. Flex items cannot become narrower than
This is fairly hard to see. Once you add the
min-width: 25% the items above are shrunk, but to a minimum of 25%. That's why the Home item actually becomes larger: its
min-content is less than 25%. So min-width overrules shrinking.
Because you add min-width,
flex-basis is also re-interpreted as
width, which means that the Teamteam items do not grow beyond their min-width of 25% and effectively become narrower than without the min-width.
overflow to the flex container has no effect on the sizing of the flex items: they use the width of the container for layout, as usual.
aspect-ratio in flexbox works pretty much as you'd expect. It is a weak declararion; it allows itself to be overruled by any adverse condition.
In practice that means that the flex item's height may be constrained to a certain minimum by the text's height. As long as that's not the case the items retain their
aspect-ratio, but as soon as the height should become less than the content height, browsers drop
aspect-ratio, as they should.