📋 目錄
CSS Grid 是 2017 年以來最重要的 CSS 布局技術,但它有個根本限制:父元素的 grid tracks(欄與列)無法被子元素繼承。巢狀的 grid 子項目,雖然自己也是一個 grid,但它只能定義自己的欄與列,沒有辨法與父層的 grid 對齊。這個限制造成了一個長期以來無解的問題:「如何讓巢狀卡片的內容,與其他卡片保持對齊?」Subgrid 解決了這個問題。2026 年,所有主流瀏覽器都支援了,是時候把它加入你的布局工具箱。
前端工程師為什麼要關心 Subgrid
在做 UI 開發時,卡片式布局是最高頻的场景之一。每個卡片內部可能有標題、內容、作者、日期、按鈕等元素。問題來了:當卡片內容長度不一致時,卡片底部很難對齊。
過去的解決方式:
/* 方式一:flex 強迫撐高 */
.card {
display: flex;
flex-direction: column;
}
.card__content {
flex: 1; /* 讓內容區塊自動撐滿剩餘空間 */
}
/* 方式二:固定高度(不夠靈活)*/
.card {
height: 400px;
overflow: hidden;
}
這些 workaround 都治標不治本。Subgrid 的做法則是:讓整個卡片的 grid 結構由外層統一管理,卡片內部的元素直接「繼承」外層定義好的欄與列。
Subgrid 的基本語法
什麼是 Subgrid
Subgrid 的核心概念:子元素使用 subgrid 關鍵字,繼承父元素的 grid tracks。
/* 父層:定義一個 3 欄的 grid */
.card-grid {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 1.5rem;
}
/* 子層:使用 subgrid 繼承父層的 grid tracks */
.card {
display: grid;
grid-row: span 3; /* 佔滿 3 列 */
grid-template-rows: subgrid; /* 關鍵:繼承父層的 3 個 row tracks */
gap: 0; /* 由父層統一管理 gap */
}
/* 子層的內部元素,直接對齊到父層定義的 row tracks */
.card__title {
grid-row: 1; /* 對齊到第 1 個 row track */
}
.card__content {
grid-row: 2; /* 對齊到第 2 個 row track */
}
.card__footer {
grid-row: 3; /* 對齊到第 3 個 row track */
}
這個例子的關鍵:card__title、.card__content、.card__footer 這三個元素,分別對齊到父層 .card-grid 定義的三個 row tracks。無論每個卡片的內容有多長,所有卡片的標題會在同一行,所有內容會在同一行,所有按鈕會在同一行。
單方向的 Subgrid
Subgrid 可以只在其中一個方向使用:
/* 只繼承 column tracks(欄),rows 自己定義 */
.card {
display: grid;
grid-template-columns: subgrid; /* 繼承父層的 column tracks */
grid-template-rows: auto 1fr auto; /* rows 自己定義 */
}
/* 只繼承 row tracks(列),columns 自己定義 */
.card {
display: grid;
grid-template-columns: auto 1fr auto; /* columns 自己定義 */
grid-template-rows: subgrid; /* 繼承父層的 row tracks */
}
實際應用場景
1. 卡片布局:完美對齊的卡片底部
這是 Subgrid 最典型的應用場景:
<div class="card-grid">
<article class="card">
<h2 class="card__title">短標題</h2>
<p class="card__content">內容。</p>
<div class="card__footer">
<button>按鈕</button>
</div>
</article>
<article class="card">
<h2 class="card__title">這是一個非常長的標題會換行</h2>
<p class="card__content">這是更長的內容區塊。</p>
<div class="card__footer">
<button>按鈕</button>
</div>
</article>
<article class="card">
<h2 class="card__title">另一個標題</h2>
<p class="card__content">適中的內容。</p>
<div class="card__footer">
<button>按鈕</button>
</div>
</article>
</div>
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
/* 定義 3 個 row tracks */
grid-template-rows: auto 1fr auto;
}
.card {
/* 關鍵:span 到所有 3 個 row tracks,然後用 subgrid 繼承 */
grid-row: span 3;
grid-template-rows: subgrid;
/* 子元素會對齊到父層定義的 3 個 row tracks */
}
.card__title {
grid-row: 1;
font-size: 1.25rem;
font-weight: 600;
}
.card__content {
grid-row: 2;
color: #64748b;
}
.card__footer {
grid-row: 3;
margin-top: auto; /* 對齊到底部 */
}
效果:無論中間那個卡片的內容有多長,三個卡片的標題都在同一行、按鈕都在同一行。
2. 表單對齊:標籤和輸入框對齊
過去要對齊表單的標籤和輸入框,通常需要固定寬度或 flex:
/* 過去的做法 */
.form-row {
display: flex;
align-items: center;
}
.form-row label {
width: 120px; /* 固定寬度,不夠靈活 */
}
.form-row input {
flex: 1;
}
Subgrid 的做法:
<form class="form">
<div class="form-row">
<label for="name">姓名</label>
<input type="text" id="name" placeholder="張三" />
</div>
<div class="form-row">
<label for="email">電子郵件地址(很長的標籤)</label>
<input type="email" id="email" placeholder="a@example.com" />
</div>
<div class="form-row">
<label for="phone">電話</label>
<input type="tel" id="phone" placeholder="0912345678" />
</div>
</form>
.form {
display: grid;
grid-template-columns: auto 1fr; /* 標籤欄 + 輸入框欄 */
gap: 0.75rem;
/* 定義多個 row tracks,每個 row track 的高度由內容決定 */
grid-template-rows: subgrid;
grid-row: span 3; /* 表單有 3 個表單行 */
}
.form-row {
display: grid;
grid-template-columns: subgrid; /* 繼承父層的 column tracks */
gap: 0.5rem;
align-items: center;
}
.form-row label {
/* 對齊到第一個 column track */
font-weight: 500;
}
.form-row input {
/* 對齊到第二個 column track */
padding: 0.5rem;
border: 1px solid #e5e5e5;
border-radius: 4px;
}
所有表單標籤會與其對應的輸入框完全對齊,無論標籤文字有多長。
3. 圖片牆:跨列/跨行的圖片對齊
.gallery {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(3, 200px);
gap: 1rem;
}
.gallery-item {
/* 讓每個 item 繼承父層的 column tracks */
grid-column: span 2;
grid-template-columns: subgrid;
}
.gallery-item img {
grid-column: 1 / -1; /* 圖片撐滿 item 的所有 column tracks */
width: 100%;
height: 100%;
object-fit: cover;
}
4. 與 Container Queries 搭配
Subgrid 與 Container Queries(容器查詢)是天生的一對:
.card-container {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: subgrid;
grid-row: span 3;
}
/* 當容器寬度 >= 400px 時,改成 2 欄布局 */
@container card (min-width: 400px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
Subgrid vs 傳統 Grid
| 維度 | 傳統 Grid | Subgrid |
|---|---|---|
| 父子布局關係 | 子元素獨立定義自己的 tracks | 子元素繼承父元素的 tracks |
| 巢狀元件對齊 | 需要 flex workarounds | 原生支援 |
| 應用場景 | 簡單的網格布局 | 複雜的巢狀對齊需求 |
| 程式碼簡潔度 | 需要額外的 workaround | 更簡潔的佈局代碼 |
瀏覽器支援
2026 年,Subgrid 在所有主流瀏覽器都已經完全支援:
| 瀏覽器 | 支援版本 |
|---|---|
| Chrome | 117+ |
| Edge | 117+ |
| Safari | 16+ |
| Firefox | 71+(從 2019 年就支援,是第一個實現 Subgrid 的瀏覽器) |
不需要任何 Progressive Enhancement——如果你的用戶不是特別老的 Safari/Firefox 用戶,可以直接使用。
/* 如果你需要支援非常舊的瀏覽器 */
@supports (grid-template-columns: subgrid) {
.card {
grid-template-rows: subgrid;
}
}
結語:讓 CSS 布局回到正確的抽象層次
Subgrid 的價值,不只是在於「能做到什麼」,而在於「什麼東西應該由誰來決定」。
過去在 Subgrid 出現之前,卡片內部的布局由內部決定,父層無法強制對齊。這導致了一個不好的結果:卡片被迫使用 flex: 1 或固定高度這類 workaround,來實現「視覺上對齊」的效果,但這些 workaround 本身是為了修補 CSS 的不足,而不是表達佈局的本意。
Subgrid 把布局的「控制權」還給了正確的層級:父層定義整體的結構(欄與列),子層只需要把自己的內容「放進」父層定義好的結構裡。
這是 CSS 布局本應有的樣子。
*## 延伸閱讀
- Interop 2026 CSS 新特性全景 — 2026 年 CSS 新功能瀏覽器支援狀態
- CSS 完整指南 — CSS Cascade 分層管理
本文是「2026 CSS 實用技術」系列文章之一。*