align-content
works in the default layout in 2024,
allowing vertical centering with 1 CSS property.
<div style="align-content: center; height: 100px;">
<code>align-content</code> just works!
</div>
align-content
just works!
Supported
since:
Chrome: 123 |
Firefox: 125 |
Safari: 17.4
What’s new?
The status quo for CSS alignment is to switch to flexbox or
grid because align-content
doesn’t work in the
default layout (flow). In 2024, browsers have implemented
align-content
for flow layout. This has some
advantages:
- You do not need flexbox or grid, just 1 CSS property for alignment.
- Therefore, the content doesn’t need to be wrapped in a div.
<!-- Works -->
<div style="display: grid; align-content: center;">
Content.
</div>
<!-- FAIL! -->
<div style="display: grid; align-content: center;">
Content with <em>multiple</em> nodes.
</div>
<!-- Works with the content wrapper -->
<div style="display: grid; align-content: center;">
<div> <!-- The extra wrapper -->
Content with <em>multiple</em> nodes.
</div>
</div>
<!-- Works without the content wrapper -->
<div style="align-content: center;">
Content with <em>multiple</em> nodes.
</div>
It’s amazing that CSS finally has a single property to control vertical align after decades of progress!
Vertical centering — a history
Browsers are funny, basic needs like aligning stuff do not have simple answers for a very long time. Here is how to center stuff in libreoffice:
Here is how to center vertically in a browser (horizontal centering is another topic):
Method 1: table cell
Sanity: ★★★☆☆
There are 4 major layouts: flow (default), table, flexbox, grid. How to align stuff depends on the layout of the container. Flexbox and grid were added rather late, so table was the first option.
<div style="display: table;">
<div style="display: table-cell; vertical-align: middle;">
Content.
</div>
</div>
A table can be summoned purely from CSS, but it’s a shame that such an indirection was needed.
Method 2: absolute positioning
Sanity: ☆☆☆☆☆
For reasons I don’t understand. People kept inventing more indirect ways to do things.
<div style="position: relative;">
<div style="position: absolute; top: 50%; transform: translateY(-50%);">
Content.
</div>
</div>
This one uses absolute positioning to bypass the layout, since the flow layout doesn’t help us:
- Mark the reference container with
position: relative
. - Place the edge of the content at the center with
position: absolute; top: 50%
. - Offset the content center to the edge with
transform: translateY(-50%)
.
Method 3: inline content
Sanity: ☆☆☆☆☆
While the flow layout doesn’t help with content alignment. It allows vertical alignment within a line. So why not make a line as tall as the container?
<div class="container">
::before
<div class="content">Content.</div>
</div>
.container::before {
content: '';
height: 100%;
display: inline-block;
vertical-align: middle;
}
.content {
display: inline-block;
vertical-align: middle;
}
This has some flaws: besides sacrificing a pseudo-element, there’s a zero-width “strut” character at the beginning that can mess stuff up.
Method 4: single-line flexbox
Sanity: ★★★☆☆
Flexbox became widely available 2 decades after the Web has taken
off. It has 2 modes: single-line and multi-line. In single-line mode
(default), the line fills the vertical space, and
align-items
aligns stuff inside the line.
<div style="display: flex; align-items: center;">
<div>Content.</div>
</div>
Alternatively, make the line columnar and align items with
justify-content
.
<div style="display: flex; flex-flow: column; justify-content: center;">
<div>Content.</div>
</div>
Method 5: multi-line flexbox
Sanity: ★★★☆☆
In a multi-line flexbox, the line no longer fills the vertical space,
so the line (with just 1 item in it) can be aligned with
align-content
.
<div style="display: flex; flex-flow: row wrap; align-content: center;">
<div>Content.</div>
</div>
Method 6: grid content
Sanity: ★★★★☆
Grid was even later. Alignment became simpler.
<div style="display: grid; align-content: center;">
<div>Content.</div>
</div>
Method 7: grid cell
Sanity: ★★★★☆
Note the subtle difference from the previous one:
align-content
centers the cell to the container.align-items
centers the content to the cell while the cell stretches to fit the container.
<div style="display: grid; align-items: center;">
<div>Content.</div>
</div>
There seems to be many ways to do the same thing.
Method 8: auto-margin
Sanity: ★★★☆☆
In flow layout, margin:auto
centers horizontally, but
not vertically. Flexbox and grid do not share this absurdity.
<div style="display: grid;">
<div style="margin-block: auto;">
Content.
</div>
</div>
Still, it puzzled me why margin was designed to also control alignment.
Method 9: this post in 2024
Sanity: ★★★★★
Why didn’t browsers add this in the first place?
<div style="align-content: center;">
<code>align-content</code> just works!
</div>
The table cell from the Method 1, like this method, also doesn’t require a content wrapper (it requires a table wrapper, though). We’re back to square one!
Summary
All vertical centering methods in this codepen. Know other methods? Feel free to tell me.
Going 2-dimensional
Is there a single property for horizontal
align? What’s the counterpart of align-content
? Let’s take
a look at various alignment properties:
flow | flexbox | grid | |
---|---|---|---|
align-content |
block axis | cross axis (line) | block axis (grid) |
justify-content |
no effect | main axis | inline axis (grid) |
align-items |
no effect | cross axis (item) | block axis (cell) |
justify-items |
no effect | no effect | inline axis (cell) |
Background: CSS axis terminology
The block axis is usually vertical, and the
inline axis is horizontal. These terms are needed
because vertical
writing-mode
is a thing, so block axis and
inline axis are relative to the text direction. This is similar
to how main axis and cross axis are relative to the
flexbox item direction.
On naming things
From the names of properties we can infer how CSS is designed:
align-*
is mostly vertical, whilejustify-*
is mostly horizontal.*-content
and*-items
control different levels of objects?
justify-content
is the counterpart of
align-content
, which is handy in grid layout but has no
effect in flow layout. The place-content
shorthand sets both.
“Align” vs. “justify”
Why do “align” and “justify” refer to axes in CSS? Is
justify-*
inspired by text justification? It’s chaotic,
considering there’s also
text-align: justify
.
Usually when people say “align”, they mean the placement of a single object, while “justify” means the distribution of multiple objects.
While in CSS, both justify-*
and align-*
are like text justification, because they accept values like
space-between
; they just mean different axes!
How to memorize: Text justification is horizontal,
so is justify-*
.
“Content” vs. “items”
In flexbox, “content” and “items” are confusing:
- Main axis:
justify-content
controls items, whilejustify-items
has no effect. - Cross axis: differences between single-line and multi-line modes.
Conclusion: “items” is for stuff that can be aligned individually. On the main axis, flex items cannot be aligned individually, so it’s “content”.
Why is CSS so confusing?
Even if we ignore historical artifacts, CSS is still too confusing for most of us. It has hundreds of poorly named properties, each can influence the outcome in unintuitive ways.
Software projects gone haywire
This is a case study in software design paradigms:
- Unix: orthogonal, composable primitives that can be reasoned about independently.
- CSS (Central Software System): just amend the software with more and more knobs.
The early WWW was just linked documents. CSS was created to style documents without regard to layout. Over time, CSS gained some random layout features without a coherent vision.
Often you have many ways to do something in CSS, but not the right feature to do it sanely. This entire post is about a new feature for saner vertical align, and the horizontal axis is still different.
In contrast, libreoffice follows the paradigm of orthogonal, composable primitives:
- Alignment is unified. No point that vertical is different from horizontal.
- Unified alignment is possible because “align” and “justify” are
orthogonal, not mixed up.
- “Align” is a property of the container.
- “Justify” is a property of the paragraph.
- “Align” and “justify” can be combined in any way.
libreoffice align | libreoffice justify |
---|---|
CSS mastery takes effort!
Still, rules can be learned, even for something as incomprehensible as CSS. You just need to pay extra attention instead of relying on trial-and-error and copy-pasting. I’m creating a visual guide to the hard parts of CSS. Check it out!