2024年CSS終於實現垂直置中

在2024年,align-content終於可以在塊級佈局中運作了。只需一個CSS屬性就能實現垂直置中,而不需使用Flexbox或Grid。

1
2
3
<div style="align-content: center; height: 100px;">
<code>align-content</code> 現在可以直接使用了!
</div>

支援情況:
Chrome: 123 | Firefox: 125 | Safari: 17.4

可以看到只支援比較新的瀏覽器版本,所以使用上還是得考慮一下,總的來說,雖然我們會很興奮地看到新的 CSS 特性出現,但在實際開發中,我們需要平衡新特性的使用和對舊版瀏覽器的支援。通常,我們會採用漸進式增強的策略,確保基本功能在所有環境中都能正常運作,同時為支援新特性的瀏覽器提供更好的體驗。。

有什麼新變化?

CSS對齊的現況是需要切換到flexboxgrid佈局,因為align-content在預設佈局(flow)中不起作用。在2024年,瀏覽器實作了流式佈局的align-content。這帶來一些好處:

  • 你不需要使用flexbox或grid,只需要一個CSS屬性就能實現對齊。
  • 因此,內容不需要包裝在一個div中。
1
2
3
4
<!-- 可以運作 -->
<div style="display: grid; align-content: center;">
內容。
</div>
1
2
3
4
<!-- 失敗! -->
<div style="display: grid; align-content: center;">
包含<em>多個</em>節點的內容。
</div>
1
2
3
4
5
6
<!-- 使用內容包裝器可以運作 -->
<div style="display: grid; align-content: center;">
<div> <!-- 額外的包裝器 -->
包含<em>多個</em>節點的內容。
</div>
</div>
1
2
3
4
<!-- 不需要內容包裝器就可以運作 -->
<div style="align-content: center;">
包含<em>多個</em>節點的內容。
</div>

經過幾十年的進步,CSS終於有了一個單一屬性來控制垂直對齊,這真是太棒了!

垂直置中 - 歷史回顧

瀏覽器很有趣,像對齊這樣的基本需求在很長一段時間內都沒有簡單的解決方案。以下是如何置中內容:

以下是在瀏覽器中如何垂直置中(水平置中是另一個話題):

方法1:表格單元格

有4種主要佈局:flow(預設)、table、flexbox、grid。如何對齊內容取決於容器的佈局。flexbox和grid是後來才加入的,所以table是最早的選擇。

1
2
3
4
5
<div style="display: table;">
<div style="display: table-cell; vertical-align: middle;">
內容。
</div>
</div>

可以僅透過CSS召喚出一個表格,但需要這樣一個間接方法實在是一種恥辱。

方法2:絕對定位

1
2
3
4
5
<div style="position: relative;">
<div style="position: absolute; top: 50%; transform: translateY(-50%);">
內容。
</div>
</div>

這種方法使用絕對定位來繞過佈局,因為流式佈局無法幫助我們:

  1. position: relative標記參考容器。
  2. position: absolute; top: 50%將內容邊緣放在中心。
  3. transform: translateY(-50%)將內容中心偏移到邊緣。

此方法的缺點有:

  1. 複雜性:這個方法需要多個 CSS 屬性組合,不夠直觀。
  2. 破壞文檔流:使用絕對定位會將元素從正常文檔流中移除,可能導致佈局問題。
  3. 需要額外的包裝元素:這個方法通常需要一個額外的容器元素。
  4. 不靈活:在某些情況下,這種方法可能不適用,特別是當容器高度不固定時。
  5. 性能考慮:使用 transform 可能在某些情況下引起性能問題。
  6. 不符合語義:這種方法使用了與內容語義無關的 CSS 技巧來實現佈局。

不過這個方法還是很多開發者在使用,因為

  1. 廣泛支援:這個方法在絕大多數瀏覽器中都能正常工作,包括較舊的版本。
  2. 可靠性:一旦掌握,這個方法在大多數情況下都能可靠地工作。
  3. 靈活性:可以輕易調整以適應不同的情況,如水平置中。
  4. 習慣:許多開發者已經習慣了這種方法,並能快速實現。
  5. 相容性好:不依賴於新的 CSS 特性,因此在各種環境中都能使用。
  6. 問題解決:儘管不完美,但它解決了一個常見的佈局問題。

方法3:行內內容

雖然流式佈局不能幫助內容對齊,但它允許在行內進行垂直對齊。那麼為什麼不讓一行和容器一樣高呢?

1
2
3
4
<div class="container">
::before
<div class="content">內容。</div>
</div>
1
2
3
4
5
6
7
8
9
10
.container::before {
content: '';
height: 100%;
display: inline-block;
vertical-align: middle;
}
.content {
display: inline-block;
vertical-align: middle;
}

這有一些缺陷:除了犧牲一個偽元素外,開頭還有一個零寬度的"支柱"字元可能會搞亂東西。

方法4:單行flexbox

flexbox在Web出現20年後才廣泛可用。它有兩種模式:單行和多行。在單行模式(預設)下,該行填滿垂直空間,align-items在行內對齊內容。

1
2
3
<div style="display: flex; align-items: center;">
<div>內容。</div>
</div>

或者,讓行變成列,用justify-content對齊項目。

1
2
3
<div style="display: flex; flex-flow: column; justify-content: center;">
<div>內容。</div>
</div>

方法5:多行flexbox

在多行flexbox中,行不再填滿垂直空間,所以可以用align-content對齊只有1個項目的行。

1
2
3
<div style="display: flex; flex-flow: row wrap; align-content: center;">
<div>內容。</div>
</div>

方法6:網格內容

網格出現得更晚。對齊變得更簡單。

1
2
3
<div style="display: grid; align-content: center;">
<div>內容。</div>
</div>

方法7:網格單元格

注意與前一種方法的細微差別:

  • align-content單元格相對於容器置中。
  • align-items內容相對於單元格置中,而單元格會拉伸以適應容器
1
2
3
<div style="display: grid; align-items: center;">
<div>內容。</div>
</div>

似乎有很多方法可以做同樣的事情。

方法8:自動外邊距

在流式佈局中,margin:auto可以水平置中,但不能垂直置中。flexbox和Grid沒有這個問題。

1
2
3
4
5
<div style="display: grid;">
<div style="margin-block: auto;">
內容。
</div>
</div>

不過,我還是很困惑為什麼要設計邊距來控制對齊。

方法9:2024年的這篇文章

為什麼瀏覽器一開始就不加入這個功能呢?

1
2
3
<div style="align-content: center;">
<code>align-content</code> 現在可以直接使用了!
</div>

方法1中的表格單元格,像這種方法一樣,也不需要內容包裝器(儘管它需要一個表格包裝器)。我們又回到了起點!

不過使用上要看瀏覽器是否支援,可以使用以下策略:

  1. 使用特性檢測:
    1
    2
    3
    4
    5
    if ('alignContent' in document.documentElement.style) {
    // 使用新的 align-content 方法
    } else {
    // 使用備用方法
    }
  2. 使用 CSS @supports:
    1
    2
    3
    4
    5
    6
    7
    8
    9

    @supports (align-content: center) {
    .container {
    align-content: center;
    }
    }
    @supports not (align-content: center) {
    /* 當樣式不被瀏覽器所支援就套用此處的樣式 */
    }

    總結

以下是所有垂直置中方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<p>Various methods for vertical centering. Yellow is container and green is content wrapper. Note that the first and last methods do not require a content wrapper.</p>

<div class="container" style="display: table;">
<div style="display: table-cell; vertical-align: middle;">
Table cell with <code>vertical-align</code>.
</div>
</div>

<div class="container" style="position: relative;">
<div class="content" style="position: absolute; top: 50%; transform: translateY(-50%);">
<b>Absolute</b> positioning.
</div>
</div>

<div class="container">
<span style="display: inline-block; vertical-align: middle; height: 100%;"></span><div class="content" style="display: inline-block; vertical-align: middle;">
<b>Inline</b> content.
</div>
</div>

<div class="container" style="display: flex; align-items: center;">
<div class="content">Single-line <b>flexbox</b> (cross axis).</div>
</div>

<div class="container" style="display: flex; flex-flow: column; justify-content: center;">
<div class="content">Single-line <b>flexbox</b> (main axis).</div>
</div>

<div class="container" style="display: flex; flex-flow: row wrap; align-content: center;">
<div class="content">Multi-line <b>flexbox</b> (cross axis).</div>
</div>

<div class="container" style="display: grid; align-content: center;">
<div class="content"><b>Grid</b> content.</div>
</div>

<div class="container" style="display: grid; align-items: center;">
<div class="content"><b>Grid</b> cell.</div>
</div>

<div class="container" style="display: grid;">
<div class="content" style="margin-block: auto;">
Auto-<b>margin</b>.
</div>
</div>

<div class="container" style="align-content: center;">
<code>align-content</code> just works in 2024!
</div>

實現二維對齊

有沒有一個水平對齊的單一屬性?align-content的對應屬性是什麼?讓我們看看各種對齊屬性:

flow flexbox grid
align-content block軸 交叉軸(行) block軸(網格)
justify-content 無效果 主軸 inline軸(網格)
align-items 無效果 交叉軸(項目) block軸(單元格)
justify-items 無效果 無效果 inline軸(單元格)

表:不同佈局中的對齊屬性

背景:CSS軸術語

block軸通常是垂直的inline軸水平的。需要這些術語是因為垂直writing-mode是存在的,所以block軸和inline軸是相對於文字方向的。這類似於主軸交叉軸是相對於flexbox項目方向的。

結論: "items"用於可以單獨對齊的內容。在主軸上,flex項目不能單獨對齊,所以是"content"。