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.

enter image description here


align-content and align-items working together

Restore align-items: center. Now when items wrap, there is a visible gap between lines.

enter image description here

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):

enter image description here

... and continue here (with align-content: stretch and align-items: center):

enter image description here

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.

enter image description here


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