2024 年起,align-content 終於可以在塊級佈局裡運作了。在這之前,想單靠一個屬性垂直置中是辦不到的,你得先把容器換成 Flexbox 或 Grid。
1 | <div style="align-content: center; height: 100px;"> |
注意上面那個 height: 100px,它不是裝飾。align-content 做的事情是「把內容在 block 軸上的剩餘空間裡分配」,前提是容器得真的有剩餘空間。普通的塊級元素高度是 auto,會被內容撐到剛剛好、一點空間都不剩,這時候寫 align-content: center 不會有任何視覺變化。要看到效果,容器至少要有明確的 height、min-height,或是被外層(例如 grid track、flex item)給定一個比內容更大的高度。這個前提我第一次踩到時找了快二十分鐘,明明屬性寫對了卻沒反應。
瀏覽器支援狀況:Chrome 123、Firefox 125、Safari 17.4,都是 2024 年第二季才到位。覆蓋率還沒到 100%,生產環境得做漸進式增強——先讓舊瀏覽器也能正常置中,再讓新瀏覽器走更乾淨的寫法。後面會講到這裡有個陷阱,多數人以為的 @supports 寫法其實是錯的。
有什麼新變化?
過去在塊級佈局(flow)裡寫 align-content 是沒效果的,你得切換到 flexbox 或 grid。2024 年瀏覽器替流式佈局實作了 align-content,帶來兩個好處:
- 不必切換佈局,一個屬性直接生效。
- 內容不必再為了置中多包一層
div。
順帶說一個老陷阱。單行 flexbox(預設 nowrap)裡,align-content 完全沒效果——整行已經撐滿交叉軸,沒有多餘空間可以分配:
1 | <!-- 沒效果:單行 flexbox 的 align-content 不起作用 --> |
很多教學會教你「改成多行 flexbox(flex-flow: row wrap)讓行不再撐滿,align-content 就有事可做」。這招技術上行得通,但如果你的目的只是垂直置中,它其實是繞遠路:wrap 會改變整個容器的換行行為,多項目的 flex 容器一旦允許換行,排版可能整個跑掉。真正想置中,直接用 align-items 就好——align-items 控制的是子項目在交叉軸上的對齊位置,在單行 flexbox 下就生效,不需要動換行:
1 | <!-- 想置中就用這個:align-items 在單行 flexbox 直接生效 --> |
當然,如果你確實需要多行排版又要整組行置中,那 align-content 配 wrap 才是對的工具,看需求選。
2024 年起,連佈局都不用換,流式佈局裡直接寫就行(記得容器要有高度):
1 | <!-- 不需要切換佈局、也不需要額外包裝器 --> |
折騰幾十年,CSS 終於能用一個屬性搞定垂直對齊。
垂直置中:歷史回顧
對齊這種基本需求,CSS 長期以來就是沒有簡單解法,這點蠻反直覺的。以下是從古至今的垂直置中寫法(水平置中是另一個故事)。每一種我都附上容器高度,因為少了高度多半看不出效果。
方法 1:表格單元格
CSS 有 4 種主要佈局:flow(預設)、table、flexbox、grid。flexbox 和 grid 是後來才加的,所以最早能用的選擇是 table。
1 | <div style="display: table; height: 100px;"> |
純靠 CSS 召喚出一張表格來置中,能用,但有點迂迴。
方法 2:絕對定位
1 | <div style="position: relative; height: 100px;"> |
流式佈局幫不上忙,所以繞過去:
- 用
position: relative標記參考容器。 position: absolute; top: 50%把內容上緣移到容器中心。transform: translateY(-50%)把內容往上拉回自身高度的一半。
top: 50% 是相對父容器算的,translateY(-50%) 是相對元素自身算的,兩個百分比參照不同,配合起來剛好置中。這個方法有兩個限制要記牢:
- 父容器必須有定位上下文(
position: relative/absolute/fixed),否則position: absolute的子元素會往上找到更遠的祖先當參照。 - 子元素脫離了正常流,不再撐起父容器。如果父容器高度是 auto、本來靠這個子元素撐高,現在會直接塌成 0 高度,
top: 50%的 50% 就變成 0 的一半,置中也跟著失效。所以這招幾乎都得搭配父容器明確給高度。
用的人越來越少了,但相容性要求高的舊專案還是會碰到。
方法 3:行內內容
流式佈局不能做內容對齊,但可以在行內垂直對齊。那就讓一個行內元素和容器一樣高:
1 | <!-- ::before 是 CSS 偽元素,不是真實的 HTML 節點; |
1 | .container { |
這個方法有個小地雷:除了犧牲一個偽元素,因為 .content 是 inline-block,兩個元素之間的原始碼換行會被當成一個空白字元,可能造成意外的間距。處理方式是把標籤之間的空白擠掉,或把父容器 font-size 設成 0。
方法 4:單行 flexbox
flexbox 在 Web 問世 20 年後才廣泛可用。單行模式(預設)下,整行填滿交叉軸,用 align-items 在交叉軸對齊:
1 | <div style="display: flex; align-items: center; height: 100px;"> |
或者把主軸轉成縱向,改用 justify-content:
1 | <div style="display: flex; flex-flow: column; justify-content: center; height: 100px;"> |
方法 5:多行 flexbox(不推薦)
多行模式下,行不再填滿交叉軸,這時 align-content 才有剩餘空間可以分配,連只有一行也能置中:
1 | <div style="display: flex; flex-flow: row wrap; align-content: center; height: 100px;"> |
這是技術上行得通的路,但不建議用它來做垂直置中:flex-wrap: wrap 會改變容器的換行行為,多項目時排版可能整個跑掉。如果目的只是垂直置中,方法 4 的 align-items 是更直接的選擇。
方法 6:網格內容對齊
grid 出現得更晚,對齊變得直接:
1 | <div style="display: grid; align-content: center; height: 100px;"> |
方法 7:網格項目對齊
和前一種有個容易搞混的差異,這裡得把術語講準,不然會誤解 grid 的運作:
align-content:對齊的是整組網格軌道(grid tracks)相對於容器的位置。只有當所有軌道的總高度小於容器時才看得出效果。align-items:對齊的是網格項目(grid item)在自己所屬網格區域(grid area)內的位置。預設項目會被拉伸填滿所屬區域,設成center才會縮回內容尺寸並置中。
1 | <div style="display: grid; align-items: center; height: 100px;"> |
簡單記:align-content 動的是「軌道在容器裡的位置」,align-items 動的是「項目在自己格子裡的位置」。
方法 8:自動外邊距
流式佈局裡 margin: auto 只能水平置中,不能垂直。flexbox 和 grid 沒有這個限制:
1 | <div style="display: grid; height: 100px;"> |
用邊距控制對齊,每次看到還是覺得奇妙。
方法 9:2024 年的寫法
繞了一大圈,回到一個屬性:
1 | <div style="align-content: center; height: 100px;"> |
方法 1 的表格單元格和這個方法一樣,都不需要內容包裝器(雖然 table 需要一層表格容器)。兜了一圈,回到起點。
接下來是最容易寫錯的部分:相容性處理。
漸進式增強的真正寫法
你可能想用 JavaScript 偵測支援度,這條路一開始就堵死:
1 | // ⚠️ 這個偵測沒用 |
align-content 這個屬性名稱長期以來都在(flexbox/grid 早就支援),所以 JavaScript 的 in 運算子完全分不出「支援 block context」和「只支援 flexbox/grid」。
@supports 也救不了你,而且這是大坑——很多人以為可以這樣寫:
1 | /* ⚠️ 這段是錯的,別照抄 */ |
問題出在 @supports (align-content: center) 測的是「這個屬性值合不合法」,而 align-content: center 在 flexbox/grid 時代就是合法值了。結果是:所有支援 flex/grid 的瀏覽器都會回傳 true,包含那些只支援 flex/grid align-content、還不支援 block context 的舊瀏覽器。這些舊瀏覽器一進到 @supports 區塊就被改成 display: block,它們又不懂 block 的 align-content,於是 grid 的保底直接被蓋掉,置中失效。等於這條 @supports 把唯一能用的 fallback 親手砸了。Chrome 官方那篇支援說明也直接點明:block context 的 align-content 沒辦法用 feature query 偵測,feature query 永遠回傳 true。
既然沒有可靠的特性偵測,正確做法就是別去偵測它。把能在所有目標瀏覽器運作的版本當作唯一基底,不要用 @supports 去翻轉:
1 | /* 唯一基底:grid,新舊瀏覽器都吃得下、都能正確置中 */ |
display: grid + align-content: center 在 2017 年左右就全面可用,等於拿一個老牌、覆蓋率近乎滿分的寫法當保底,視覺結果跟 block context 版本完全一樣。少了「block context 不用切換佈局」這個語意上的乾淨,但讀者拿到的是會動的程式碼,不是一段在舊瀏覽器悄悄爛掉的 @supports。
如果你就是想要 block context 的純粹寫法,務實的判斷是:等到目標客群的瀏覽器覆蓋率夠了(看 caniuse 的數字決定),直接無條件寫 align-content: center 就好。在那之前,grid 保底是唯一不會自爆的選擇。
九種方法一次看
把前面九種寫法湊在一起,可以直接貼進 HTML 看效果(容器都給了高度才看得到差異):
1 | <p>垂直置中的各種方法。黃色是容器,綠色是內容包裝器。注意第一種和最後一種不需要內容包裝器。</p> |
二維對齊
有沒有對應的單一屬性處理水平對齊?看一下各對齊屬性在三種佈局的效果:
| flow | flexbox | grid | |
|---|---|---|---|
align-content |
block 軸 | 交叉軸(行) | block 軸(軌道) |
justify-content |
無效果 | 主軸 | inline 軸(軌道) |
align-items |
無效果 | 交叉軸(項目) | block 軸(項目於區域內) |
justify-items |
無效果 | 無效果 | inline 軸(項目於區域內) |
對應到 flow 佈局:align-content 管垂直(block 軸),但目前沒有對應的單一屬性能在 flow 裡管水平置中。水平方向還是得靠 margin: auto 或切換到 flex/grid。
背景:CSS 軸術語
block 軸通常是垂直的,inline 軸是水平的。會需要這組術語,是因為垂直 writing-mode 真的存在,所以 block 軸和 inline 軸是相對於文字方向定義的,不是寫死成上下左右。這就像 flexbox 的主軸和交叉軸是相對於排列方向,而不是固定方位。
至於 content 和 items 的命名差異:items 用在「可以各自獨立對齊」的東西上。flexbox 主軸方向的 flex 項目沒辦法各自分開對齊(它們共用同一條主軸的空間分配),所以那個方向用的是 content。
小結
九種寫法繞了一大圈,2024 年的 align-content 確實把垂直置中收斂成一個屬性,但兩個前提得記住:容器要有可分配的 block 軸空間,而且 block context 的支援度沒辦法用 @supports 偵測。在覆蓋率追上之前,拿 display: grid + align-content: center 當無條件保底,是目前唯一不會在舊瀏覽器悄悄失效的寫法。




