extra space when centering elements using flexbox
align-content
Note that align-content: stretch
is also in play here.
The align-content
property distributes free space among flex lines. It's default value is stretch
.
So when your items wrap to two lines, align-content: stretch
distributes free space equally across lines.
align-items
Remove align-items: center
from the container. You'll then notice that when items wrap, there is no gap between lines (or "rows", in this case). demo
Line heights are set by align-content
, the height of the items, and the content of the items.
align-content
and align-items
working together
Restore align-items: center
. Now when items wrap, there is a visible gap between lines.
This is because align-items: center
positions the items in the vertical center of the line, based on line heights established by align-content: stretch
, item heights, and item content.
The line heights are set here (with align-content: stretch
and align-items: stretch
):
... and continue here (with align-content: stretch
and align-items: center
):
align-items
is having no effect on the height of the flex line. That job is andled by align-content
.
However, by changing the value of align-content
(to flex-start
, flex-end
, space-between
, center
, etc.), you pack the flex lines, squeezing out the free space, and hence removing the gaps.
Why is the first row taller than the second row with align-content: stretch
?
The container is set to height: 800px
.
There are two items with defined heights:
.item-3 { height: 250px }
.item-4 { height: 300px }
When .item-5
and .item-6
form the second row, the free space in the container is, in its simplest form, 500px (800px - 300px).
So, in a container with two rows and align-content: stretch
, 250px is distributed to the height of each row.
- first row is 300px (defined height) plus 250px (free space)
- second row is 250px (free space)
That's why the first row is taller.
Just note that the free space calculation above is slightly off in the actual layout. That's because there is text in the items, which consumes free space. You can factor in the height of the text to get the precise figures, or remove the text altogether to see the calculations above fall into place.
More details:
- How does flex-wrap work with align-self, align-items and align-content?
- Equal height rows in a flex container
To make it easy, I will reduce the code so we can better understand the behavior.
Let's consider this initial code:
* {
box-sizing: border-box;
font-size: 1.5rem;
}
html {
background: #b3b3b3;
padding: 5px;
}
body {
background: #b3b3b3;
padding: 5px;
margin: 0;
}
.flex-container {
background: white;
padding: 10px;
border: 5px solid black;
height: 400px;
display: flex;
flex-direction: row;
flex-wrap:wrap;
}
.item-1 {
background: #ff7300;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
}
.item-2 {
background: #ff9640;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
width: 250px;
}
.item-3 {
background: #ff9640;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
height: 150px;
}
<div class="flex-container">
<div class="item-1">div</div>
<div class="item-2">w=250px</div>
<div class="item-3">h=150px</div>
</div>
The container has a fixed height and only one element has a fixed height and the other will be stretched to fill the parent height since by default align-items
is stretch
.
Now let's decrease the width of the container in order to make the third element in the second row:
* {
box-sizing: border-box;
font-size: 1.5rem;
}
html {
background: #b3b3b3;
padding: 5px;
}
body {
background: #b3b3b3;
padding: 5px;
margin: 0;
}
.flex-container {
background: white;
padding: 10px;
border: 5px solid black;
height: 400px;
display: flex;
flex-direction: row;
flex-wrap:wrap;
width:500px;
}
.item-1 {
background: #ff7300;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
}
.item-2 {
background: #ff9640;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
width: 250px;
}
.item-3 {
background: #ff9640;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
height: 150px;
}
<div class="flex-container">
<div class="item-1">div</div>
<div class="item-2">w=250px</div>
<div class="item-3">h=150px</div>
</div>
Here we have a complex behavior where we have a multiline flex container where item1
and item2
belong to the first line and item3
to the second line. I cannot explain very well how the height of each line is defined (the complex part). After this each flex item will stretch to fill the height of its line unless it has a fixed height like item3
or we change the alignment.
Now, if we change align-items
to something different than stretch
we will have the gap:
* {
box-sizing: border-box;
font-size: 1.5rem;
}
html {
background: #b3b3b3;
padding: 5px;
}
body {
background: #b3b3b3;
padding: 5px;
margin: 0;
}
.flex-container {
background: white;
padding: 10px;
border: 5px solid black;
height: 400px;
display: flex;
flex-direction: row;
flex-wrap:wrap;
width:500px;
align-items:flex-start;
}
.item-1 {
background: #ff7300;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
}
.item-2 {
background: #ff9640;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
width: 250px;
}
.item-3 {
background: #ff9640;
color: white;
padding: 10px;
border: 5px solid black;
margin: 10px;
height: 150px;
}
<div class="flex-container">
<div class="item-1">div</div>
<div class="item-2">w=250px</div>
<div class="item-3">h=150px</div>
</div>
If we compare the above code with the previous one we can see that item3
kept his place and only the height of item1
and item2
have changed. This explain that align-items
will align items inside their lines that was previously defined due to wrapping.
In other words, when we have a multiline flex container we first define the lines (considering the height of the container, the height of elements and other flexbox properties) then we align the items inside their line and the gap we have is due to how the alignment is done.
Here is a better example to show different cases:
.flex-container {
background: white;
padding: 10px;
border: 5px solid black;
height: 200px;
display: inline-flex;
vertical-align:top;
flex-direction: row;
flex-wrap:wrap;
width:100px;
}
.item-1 {
background: #ff7300;
color: white;
border: 1px solid black;
margin: 2px;
}
.item-2 {
background: #ff9640;
color: white;
border: 1px solid black;
margin: 2px;
width: 70px;
}
.item-3 {
background: #ff9640;
color: white;
border: 1px solid black;
margin: 2px;
height: 50px;
}
<div class="flex-container">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:flex-start;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:center;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:flex-end;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:flex-end;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3" style="margin-bottom:auto;">C</div>
</div>
<div class="flex-container">
<div class="item-1" style="margin-top:auto;">A</div>
<div class="item-2">B</div>
<div class="item-3" >C</div>
</div>
We can clearly notice that we have the same lines accross all the container and only the alignment is changing which create different gaps.
In case there is no element with a fixed height, the lines will have the same height, so the container will be splitted equally.
.flex-container {
background: white;
padding: 10px;
border: 5px solid black;
height: 200px;
display: inline-flex;
vertical-align:top;
flex-direction: row;
flex-wrap:wrap;
width:100px;
}
.item-1 {
background: #ff7300;
color: white;
border: 1px solid black;
margin: 2px;
}
.item-2 {
background: #ff9640;
color: white;
border: 1px solid black;
margin: 2px;
width: 70px;
}
.item-3 {
background: #ff9640;
color: white;
border: 1px solid black;
margin: 2px;
}
<div class="flex-container">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:flex-start;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:center;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:flex-end;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-items:flex-end;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3" style="margin-bottom:auto;">C</div>
</div>
<div class="flex-container">
<div class="item-1" style="margin-top:auto;">A</div>
<div class="item-2">B</div>
<div class="item-3" >C</div>
</div>
By changing align-content
we will change how the lines are created before considering align-items
to align the items inside their lines:
.flex-container {
background: white;
padding: 10px;
border: 5px solid black;
height: 200px;
display: inline-flex;
vertical-align:top;
flex-direction: row;
flex-wrap:wrap;
width:100px;
}
.item-1 {
background: #ff7300;
color: white;
border: 1px solid black;
margin: 2px;
}
.item-2 {
background: #ff9640;
color: white;
border: 1px solid black;
margin: 2px;
width: 70px;
}
.item-3 {
background: #ff9640;
color: white;
border: 1px solid black;
margin: 2px;
height:50px;
}
<div class="flex-container">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-content:flex-start;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-content:center;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
<div class="flex-container" style="align-content:flex-end;">
<div class="item-1">A</div>
<div class="item-2">B</div>
<div class="item-3">C</div>
</div>
In order to understand the complex part of this answer (how lines are defined) you can refer to the specification : https://www.w3.org/TR/css-flexbox-1/#layout-algorithm