How does flex-wrap work with align-self, align-items and align-content?
Short Answer
Although the flex-wrap
property seems pretty basic – it controls whether flex items can wrap – it actually has a wide-ranging impact on the entire flexbox layout.
The flex-wrap
property determines the type of flex container you will use.
-
flex-wrap: nowrap
creates a single-line flex container -
flex-wrap: wrap
andwrap-reverse
create a multi-line flex container
The align-items
and align-self
properties work in both single- and multi-line containers. However, they can only have an effect when there's free space in the cross axis of the flex line.
The align-content
property works only in multi-line containers. It is ignored in single-line containers.
Explanation
The flexbox specification provides four keyword properties for aligning flex items:
align-items
align-self
align-content
justify-content
To understand the functions of these properties it's important to first understand the structure of a flex container.
Part 1: Understanding the Main Axis and Cross Axis of a Flex Container
The X and Y Axes
A flex container works in two directions: x-axis (horizontal) and y-axis (vertical).
Source: Wikipedia
The child elements of a flex container – known as "flex items" – can be aligned in either direction. (Ignore the z axis in the image above. It doesn't apply here.)
This is flex alignment at its most fundamental level.
The Main and Cross Axes
Overlaying the x and y axes are, in flex layout, the main and cross axes.
By default, the main axis is horizontal (x-axis), and the cross axis is vertical (y-axis). That's the initial setting, as defined by the flexbox specification.
Source: W3C
However, unlike the x and y axes, which are fixed, the main and cross axes can switch directions.
The flex-direction
Property
In the image above, the main axis is horizontal and the cross axis is vertical. As mentioned earlier, that's an initial setting of a flex container.
However, these directions can be easily switched with the flex-direction
property. This property controls the direction of the main axis; it determines whether flex items align vertically or horizontally.
From the spec:
5.1. Flex Flow Direction: the
flex-direction
propertyThe
flex-direction
property specifies how flex items are placed in the flex container, by setting the direction of the flex container’s main axis. This determines the direction in which flex items are laid out.
There are four values for the flex-direction
property:
/* main axis is horizontal, cross axis is vertical */
flex-direction: row; /* default */
flex-direction: row-reverse;
/* main axis is vertical, cross axis is horizontal */
flex-direction: column;
flex-direction: column-reverse;
The cross axis is always perpendicular to the main axis.
Part 2: Flex Lines
Within the container, flex items exist in a line, known as a "flex line".
A flex line is a row or column, depending on flex-direction
.
A container can have one or more lines, depending on flex-wrap
.
Single-Line Flex Container
flex-wrap: nowrap
establishes a single-line flex container, in which flex items are forced to stay in a single line (even if they overflow the container).
The image above has one flex line.
flex-container {
display: flex;
flex-direction: column;
flex-wrap: nowrap; /* <-- allows single-line flex container */
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
width: 50px;
margin: 5px;
background-color: lightgreen;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
Multi-Line Flex Container
flex-wrap: wrap
or wrap-reverse
establishes a multi-line flex container, in which flex items can create new lines.
The image above has three flex lines.
flex-container {
display: flex;
flex-direction: column;
flex-wrap: wrap; /* <-- allows multi-line flex container */
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
width: 50px;
margin: 5px;
background-color: lightgreen;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
The image above has three flex lines.
flex-container {
display: flex;
flex-direction: row;
flex-wrap: wrap; /* <-- allows multi-line flex container */
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
Part 3: Keyword Alignment Properties
Properties are Assigned to the Main and Cross (not X and Y) Axes
While the flex-direction
property controls the direction in which flex items are laid out, there are four properties that control alignment and positioning. These are:
align-items
align-self
align-content
justify-content
Each one of these properties is permanently assigned to an axis.
The justify-content
property works only in the main axis.
The three align-*
properties work only in the cross axis.
It's a common mistake to assume that these properties are fixed to the x and y axes. For example, justify-content
is always horizontal, and align-items
is always vertical.
However, when flex-direction
is switched to column
, the main axis becomes the y-axis, and justify-content
works vertically.
The focus of this post is cross-axis alignment. For an explanation of main-axis alignment and the justify-content
property, see this post:
- In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
Definitions
The flexbox specification provides three keyword properties for cross-axis alignment:
align-items
align-self
align-content
align-items
/ align-self
The align-items
property aligns flex items along the cross axis of the flex line. It applies to flex containers.
The align-self
property is used to override align-items
on individual flex items. It applies to flex items.
Here's the definition from the spec:
8.3. Cross-axis Alignment: the
align-items
andalign-self
propertiesFlex items can be aligned in the cross axis of the current line of the flex container, similar to
justify-content
but in the perpendicular direction.align-items
sets the default alignment for all of the flex container’s items.align-self
allows this default alignment to be overridden for individual flex items.
There are six possible values for align-items
/ align-self
:
flex-start
flex-end
center
baseline
stretch
-
auto
(align-self
only)
(For a description of each value, click on the spec definition heading above.)
The initial value of align-items
is stretch
, meaning flex items will expand the full available length of the container's cross axis.
The initial value of align-self
is auto
, meaning it inherits the value of align-items
.
Below is an illustration of the effect of each value in a row-direction container.
source: W3C
align-content
This property is slightly more complex than align-items
and align-self
.
Here's the definition from the spec:
8.4. Packing Flex Lines: the
align-content
propertyThe
align-content
property aligns a flex container’s lines within the flex container when there is extra space in the cross-axis, similar to howjustify-content
aligns individual items within the main-axis. Note, this property has no effect on a single-line flex container.
In contrast to align-items
and align-self
, which move flex items within their line, align-content
moves flex lines within the container.
There are the six possible values for align-content
:
flex-start
flex-end
center
space-between
space-around
stretch
(For a description of each value, click on the spec definition heading above.)
Below is an illustration of the effect of each value in a row-direction container.
source: W3C
Why does align-content
work only in multi-line flex containers?
In a single-line flex container, the cross size of the line is equal to the cross size of the container. This means there is no free space between the line and the container. As a result, align-content
can have no effect.
Here's the relevant section from the spec:
Only multi-line flex containers ever have free space in the cross-axis for lines to be aligned in, because in a single-line flex container the sole line automatically stretches to fill the space.
Part 4: Examples Explained
In Example #1, align-self
works with flex-wrap: nowrap
because the flex items exist in a single-line container. Therefore, there is one flex line and it matches the height of the container.
flex-container {
display: flex;
flex-wrap: nowrap;
align-content: flex-start;
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
flex-item:last-child {
align-self: flex-end;
background-color: crimson;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
In Example #2, align-self
fails because it exists in a multi-line container (flex-wrap: wrap
) and align-content
is set to flex-start
. This means that flex lines are packed tightly to the start of the cross axis, leaving no free space for align-self
to work.
flex-container {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
flex-item:last-child {
align-self: flex-end;
background-color: crimson;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
The explanation for Example #3 is the same as for Example #1.
flex-container {
display: flex;
flex-wrap: nowrap;
align-items: flex-end;
align-content: flex-start;
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
The explanation for Example #4 is the same as for Example #2.
flex-container {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
align-content: flex-start;
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
Example #5 is a single-line container. As such, the cross size of the flex line equals the cross size of the container, leaving no extra space between the two. Therefore, align-content
, which aligns flex lines when there is extra space in the cross axis, is having no effect.
flex-container {
display: flex;
flex-wrap: nowrap;
align-content: center;
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
The initial setting for align-content
is stretch
. This means that if no other value is specified, the container will distribute available space evenly among flex lines. (A similar effect is created on the main axis when all flex items get flex: 1
.)
This distribution of space among lines can cause wide gaps between rows / columns. Less lines result in wider gaps. More lines result in smaller gaps, as each line gets a smaller share of the space.
To resolve this problem switch from align-content: stretch
to align-content: flex-start
. This packs the lines together (see examples #2 and #4 above). Of course, this also eliminates any free space in the line, and align-items
and align-self
can no longer work.
Here's a related post: Remove space (gaps) between multiple lines of flex items when they wrap
flex-container {
display: flex;
flex-wrap: wrap;
/* align-content: center; */
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
As explained in previous examples, with align-content: stretch
, there can be extra space in the flex lines, which allows align-items
and align-self
to work. Any other value for align-content
would pack the lines, eliminating extra space, and making align-items
and align-self
useless.
flex-container {
display: flex;
flex-wrap: wrap;
/* align-content: center; */
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
flex-item:nth-child(even) {
align-self: flex-end;
background-color: crimson;
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
First of all, align-content is useless if there is no wrap:
w3c doc
The align-content property aligns a flex container’s lines within the flex container when there is extra space in the cross-axis, similar to how justify-content aligns individual items within the main-axis. Note, this property has no effect on a single-line flex container.
Also, in your second case, align-self is not failing. It's useless because the inner lines have been packed. Let's create an space so that the style can work:
flex-container {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
flex-item:last-child {
align-self: flex-end;
background-color: crimson;
}
flex-item:nth-child(5) {
height: 90px; /* added */
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
Similarly, in your 4th example, the style is working, if you set the conditions where it can be seen
flex-container {
display: flex;
flex-wrap: wrap;
align-items: flex-end;
align-content: flex-start;
width: 250px;
height: 250px;
background-color: silver;
}
flex-item {
flex: 0 0 50px;
height: 50px;
margin: 5px;
background-color: lightgreen;
}
flex-item:nth-child(2),flex-item:nth-child(5) {
height: 90px; /* added */
}
<flex-container>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
<flex-item></flex-item>
</flex-container>
Finally, when you comment the align-content, it reverts to stretch, that's why the lines increase the size to fill up the container
stretch Lines stretch to take up the remaining space. If the leftover free-space is negative, this value is identical to flex-start. Otherwise, the free-space is split equally between all of the lines, increasing their cross size.