CSS 版面配置和回應式設計

  1. 1 基本概念
    1. 該使用哪種版面配置模式?
    2. 工作原則
  2. 2 Flexbox
  3. 3 格線和子格線
    1. 程式碼範例:格線和子格線
  4. 4 容器查詢
    1. 程式碼範例:使用容器查詢單位的流體字體排版
  5. 5 原生疊加層、錨定定位和堆疊內容
  6. 6 溢位追蹤和版面配置穩定性
  7. 7 檢視區機制和追蹤分布
  8. 8 格狀車道 (又稱磚石)

1 基礎

盡可能使用瀏覽器的版面配置引擎,以提升效能。在採用硬式編碼尺寸或複雜的媒體查詢之前,請先嘗試使用內建大小、邏輯屬性和 aspect-ratio。

1.1 該使用哪種版面配置模式?

從上到下瀏覽決策樹狀圖,並在第一個相符項目處停止。請注意,版面配置可以相互巢狀,且每項決策都是根據該容器的用途而定。

  1. 是否為簡單的項目列或欄?使用 Flexbox:1D、內容優先、內容沿著單一軸線分布。
  2. 巢狀元素是否需要與其祖項格線的軌道對齊?使用 subgrid:2D、關係優先、繼承父項軌道,讓孫項能與同層級元素對齊。
  3. 頁面或元件結構是否複雜,同時包含列和欄?使用格線:2D、版面配置優先,您定義骨架,內容則填滿骨架。
  4. 內容是否為長篇散文,應拆分為平衡的欄?使用多欄:一維流程,報紙樣式。
  5. 是否需要緊密包裝高度不一的商品?目前請搭配 grid-auto-flow: dense 使用 grid;只有在原生磚牆 (又稱「格線通道」) 推出基準目標時,才使用原生磚牆 (請參閱 §8)。
  6. 元素是否需要浮動在頁面上方,並在空間上與觸發條件保持連結,即使跨越 DOM 邊界或堆疊內容也一樣?使用錨定定位:在觸發條件上使用 anchor-name,在疊加層上使用 position-anchor (請參閱第 5 節)。

1.2 運作原理

  • 使用邏輯屬性 (inline-size、block-size、margin-inline、padding-block、inset-inline-start) 設定版面配置尺寸和間距,詳情請參閱「CSS:現代架構」。
  • 套用內容優先與版面配置優先的心智模型:當項目決定流程時,使用彈性方塊;當你先定義骨架時,使用格線。
  • 使用 place-* 簡寫 (place-content、place-items、place-self),在一個宣告中對齊兩個軸。
  • 先使用內建大小 (min-content、max-content、fit-content()) 和彈性軌道 (fr、minmax()),再使用固定的 width/height,這樣就能減少媒體查詢,並提高版面配置的彈性。
  • 請勿使用 overflow: hidden 遮蓋版面配置問題,請從來源修正。舉例來說,某些替換元素 (例如 <canvas>、<img>、<svg>、<video>、<iframe>) 預設會使用 display: inline,這可能會在某些版面配置中加入間隙。請勿使用 overflow 裁剪間隙,而是套用區塊 display-outside 值 (例如 display: block、grid、flex 等) 來移除間隙。
  • 使用 aspect-ratio 為媒體預留空間,避免素材資源載入前版面配置位移。
.sidebar       { inline-size: max-content; }    /* Size to longest unbreakable token. */
.main-content  { inline-size: fit-content; }    /* Grow to available space, no further. */
.media         { aspect-ratio: 16 / 9; inline-size: 100%; block-size: auto; }
body.centered  { display: grid; place-content: center; min-block-size: 100dvb; }

如要瞭解 calc-size() 和受限的固有大小,請參閱「使用固有大小計算」。

2 Flexbox

一維版面配置:項目會沿著單一主軸排列,並在交錯軸上對齊。適用於導覽列、工具列、項目列,以及任何單列或單欄分布。

建議做法:

  • 使用 display: flex 建立情境,並使用 flex-direction 設定主軸 (預設為 row)。
  • 只要有可能溢位,請使用 flex-wrap: wrap。如果 nowrap 沒有 overflow: auto/hidden,就會溢出到窄檢視區。
  • 在精簡項目 (例如熱門標籤集、篩選器、按鈕群組或導覽連結) 的換行資料列上,在 flex-wrap: wrap 後方加上 flex-wrap: balance,即可在各行平均分配項目,避免最後一行出現單一孤立項目;不支援的瀏覽器會改用 wrap。
  • 在項目上使用 flex 簡寫 <grow> <shrink> <basis> (例如 flex: 1 1 250px),而不是個別設定 flex-grow/flex-shrink/flex-basis。
  • 請使用 gap (或 row-gap/column-gap 長寫) 設定項目之間的間距,而非子項邊界。
  • 在位置對齊方式前加上 safe (例如 align-items: safe center),這樣當容器比內容窄時,可聚焦內容就不會遭到裁剪。
  • 使用 margin-inline-start: auto (或 margin-block-start: auto) 將單一項目推到主軸的最末端,這是標準的跳脫機制。
  • 使用 align-self 覆寫每個項目的交錯軸對齊設定。
  • 使用 align-items 將所有項目置中於交叉軸;在單一項目上使用 margin: auto,將其置中於兩個軸;僅在容器換行且各列之間有額外空間時,才使用 align-content。
  • 在包含長度過長且無法中斷內容 (網址、程式碼、長字串) 的彈性項目上設定 min-inline-size: 0 (或 min-width: 0) - 根據預設,彈性項目不會縮小到小於內容大小,導致溢位。

請勿:

  • 請勿在彈性項目上使用 justify-self,因為這項屬性僅適用於格線、區塊和絕對定位版面配置。請改用自動邊界。
  • 請勿使用 order 或 flex-direction: *-reverse 重新排序互動式內容。這些屬性只會變更視覺順序,DOM 順序仍會決定焦點順序,因此鍵盤分頁流程與使用者看到的內容不符。
  • 請勿混淆 space-around (兩端間距為一半) 和 space-evenly (前後和中間間距相等)。
  • 別忘了軸線會翻轉:當 flex-direction: column 時,justify-content 會對齊區塊軸,align-items 則會對齊內嵌軸,與預設值相反。
  • 請勿將容器和子項的大小設為填滿彼此,這是造成溢位和非預期結果的常見原因。為其中一邊指定明確大小。
  • 請勿在同一個項目上同時設定 flex-basis 和 width/inline-size,因為在彈性內容中,flex-basis 優先順序較高,系統會忽略 width。使用 flex-basis (或 flex 簡寫) 做為調整彈性項目大小的單一事實來源。
.card-grid        { display: flex; flex-flow: row wrap; gap: 1rem; }
.card-item        { flex: 1 1 250px; }                  /* grow, shrink, basis */
.card-item-action { margin-inline-start: auto; }        /* Push to main-axis end. */
.toolbar          { display: flex; align-items: safe center; }
.tag-list         { display: flex; flex-wrap: wrap; flex-wrap: balance; gap: 0.5rem; }

3 格線和子格線

二維版面配置:明確定義列和欄,或讓引擎衍生。子格線可讓巢狀格線沿用父項的軌跡,讓孫項與同層級項目對齊。

選擇格線功能:

  • 您是否知道確切需要的欄數?
    • 是:使用明確版本 (grid-template-columns: 200px 1fr、repeat(3, 1fr) 等)
    • 不同資料欄是否需要不同大小 (側欄 + 主欄、橫跨所有欄的標頭)?→ 使用 grid-template-areas 命名可讀取的區域
    • 所有資料欄是否都一致,或純粹依行號定位?→ 使用 repeat(N, ...) 或命名線
    • 否 (回應式,項目數量不明) - 使用 repeat(auto-fit, minmax(min, 1fr))
    • 最後一列的項目是否應延伸,直到填滿剩餘空間?→ auto-fit
    • 空白的最後一列軌道是否應保持最小尺寸 (保留欄幽靈版位)?→ auto-fill
  • 需要將物品放在特定位置嗎?
    • 是:使用 grid-column: <start> / <end> 或 grid-area: <name>
    • 否 (僅跨越多個軌道,流程位置不重要) — 使用 grid-column: span <n>
  • 子項元素是否需要沿用父項格線的軌道大小 (同層元素之間不規則對齊)?
    • 是:在受影響的軸上使用子格線
    • 每個儲存格的子項數量是否可變?→ 僅一個軸使用 subgrid,另一個軸使用 grid-auto-rows/grid-auto-columns
    • 子項數量是否固定?→ 兩個軸上都有子格線
    • 否:標準格線,不需要子格線

建議做法:

  • 使用 display: grid 建立情境。
  • grid-template-areas 適用於複雜的頁面層級版面配置,區域名稱可自我記錄,且宣告可對齊列和欄,一目瞭然。
  • 使用 repeat(auto-fit, minmax(200px, 1fr)) 建立回應式資訊卡格線,將填滿的軌道拉伸至填滿整列,或使用 auto-fill 將空白重複軌道保留在最小大小。
  • 使用 fr 進行比例軌道分配,並使用 minmax(min, max) 進行彈性但有界限的軌道。
  • 使用 grid-column: span <n> 調整項目在軌道上的大小、使用 grid-column: <start> / <end> 將項目放在特定線條上,或使用 grid-area: <name> 將項目放在命名區域。
  • 使用子格線 (grid-template-columns: subgrid 或 grid-template-rows: subgrid) 解決資訊卡清單中的「不規則邊緣」問題,讓標題、中繼資料和行動號召等內部元素在同層級元素之間對齊。
  • 將子格線宣告與前方的明確 grid-template-rows/-columns 宣告配對,做為舊版瀏覽器的相同階層備用方案。

請勿:

  • 請勿預期 auto-fit/auto-fill 軌道大小來自項目內容,而是來自 repeat() 大小引數。
  • 請勿在互動式內容上使用 grid-auto-flow: dense。雖然能有效率地排列項目,但會重新排序視覺效果,破壞 DOM 順序的鍵盤分頁流程。
  • 如果子項數量不固定,請勿將子格線套用至兩條軸。額外項目會放在最後一個軌道,請改用 grid-auto-rows/grid-auto-columns 做為隱含軸。
  • 請勿混淆 justify-items/align-items (對齊軌道內的項目內容) 和 justify-content/align-content (對齊容器內的格線軌道)。如果使用錯誤的屬性,系統不會顯示任何訊息,也不會產生任何效果。
  • 請勿在容器上使用 repeat(auto-fit/auto-fill, ...),而不指定明確的 inline-size。在 display: inline-grid 或未指定大小的彈性項目中,容器沒有可分割的寬度,因此無法預測軌道數量。

3.1 程式碼範例:格線和子格線

頁面殼層:<main class="page-layout"> 包含 <header>、<aside>、含有 <div class="card"> 個子項的 <section class="card-grid">,以及 <footer>。

/* Align grid-template-areas in rows and columns for readability. */
.page-layout {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas:
    "header  header  header"
    "sidebar main    main"
    "footer  footer  footer";
  gap: 1.5rem;
}

header  { grid-area: header; }
aside   { grid-area: sidebar; }
footer  { grid-area: footer; }

.card-grid {
  grid-area: main;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  grid-template-rows: auto 1fr; /* title block, body block */
  gap: 1rem;
}

.card {
  grid-row: span 2;
  display: grid;
  /* Same-cascade fallback: ignored when subgrid is supported. */
  grid-template-rows: auto 1fr;
  grid-template-rows: subgrid;
}

4 容器查詢

查詢祖系容器的大小 (或計算樣式),而非可視區域。心智模型:容器查詢 = 元件情境;媒體查詢 = 全域頁面版面配置和使用者偏好設定 (prefers-color-scheme、prefers-reduced-motion)。

建議做法:

  • 在包裝函式上使用 container-type: inline-size (僅限寬度查詢) 或 container-type: size (兩個軸) 建立容器環境,然後才能查詢其子系。
  • 如果巢狀結構定義可能發生衝突,請使用 container-name (或 container 簡寫:container: card / inline-size) 為容器命名。
  • 計算流體類型和間距時,納入容器查詢單位:cqi/cqb (邏輯內嵌/區塊)、cqw/cqh (實體)、cqmin/cqmax。
  • 只要使用 container-type: size,就必須為容器指定明確的 block-size,否則子系會收合,因為大小限制會強制容器忽略其內容。

請勿:

  • 請勿使用 block-size 做為 container-type 值,因為這不是有效值。兩個軸都使用 size。
  • 宣告 container-type 後,請勿預期子項的固有大小會影響容器。容器啟用後,系統會將容器視為沒有子項。
  • 請勿在不符合條件的祖系後代中使用容器查詢單位,因為這些單位會改用小型可視區域 (svw/svh)。

4.1 程式碼範例:使用容器查詢單位的流動字體排版

.card-wrapper {
  container: card / inline-size; /* shorthand for container-name / container-type */
}

@container card (inline-size > 400px) {
  .content {
    display: flex;
    gap: 2rem;
  }
}

.title {
  /* Fluid type bound to the container width, not the viewport. */
  font-size: clamp(1rem, 4cqi, 2rem);
}

如需元件導向的回應式樣式模式,請參閱「感知大小的樣式」和「流暢擴充」。

5 原生重疊、錨定定位和堆疊情境

各疊加層基元的適用時機:

  • 使用 popover 建立暫時性、非互動式的 UI (彈出式視窗、浮動式訊息、工具提示),這類 UI 位於頂層,不需要 z-index 管理。
  • 將 <dialog> 與 .showModal() 搭配使用,可進行需要焦點捕捉和惰性背景的互動。
  • 請勿在同一個元素上合併 popover 和 .showModal(),因為這兩者是互斥的執行階段狀態。

錨定定位 (疊加層的空間版面配置):

  • 使用 position-area (或 anchor() 內嵌) 和 anchor-size(),根據觸發條件設定疊加層的位置和大小。
  • 使用 position-try-fallbacks: flip-block (或 flip-inline),讓瀏覽器在疊加層超出可視區域時重新定位。
  • 請勿在單一 position-area 值中混用實體和邏輯關鍵字,請選擇其中一種座標系統。
  • 使用 @supports (anchor-name: --x) 偵測功能,並提供絕對位置備用方案。

如需完整的實作詳細資料、polyfill 策略和 popover 值參考資料,請參閱「宣告式對話方塊和彈出式視窗控制項」和「位置感知工具提示」。如要瞭解如何將錨定定位套用至下拉式選單、彈出式選單和分頁指標,請參閱「Viewport-Aware Dropdowns」和「Anchor Positioning Tab Underline」。

6 溢位追蹤和版面配置穩定性

以可預測的方式管理版面配置位移、捲軸和裁剪。

建議做法:

  • 使用 overflow: auto,只有在內容實際溢出時,才會顯示捲軸。
  • 使用 overflow: clip 裁剪內容,不建立捲動容器;使用 overflow-clip-margin 啟用溢出功能。
  • 使用 scrollbar-gutter: stable 為捲軸保留空間,並在內容增加時防止版面配置位移。
  • 在巢狀捲動區域 (例如側欄、互動視窗和清單) 使用 overscroll-behavior: contain (或 none),防止捲動鏈結向上傳遞至頁面,或在中間捲動包裝函式 (例如滑動顯示軌跡) 使用 overscroll-behavior: chain,以便將多餘的捲動傳遞至祖系捲動器,而不會觸發本機橡皮筋彈跳。
  • 使用 -webkit-line-clamp + display: -webkit-box + -webkit-box-orient: vertical 三元組進行多行截斷,儘管有前置字元,但這個模式已完整指定,且未淘汰。請一併宣告不含前置字元的 line-clamp 簡寫;尚未支援該屬性的瀏覽器會忽略該屬性,不會造成任何影響。請勿:

  • 如果 auto 就能解決問題,請勿使用 overflow: scroll,因為 scroll 會強制顯示捲軸,即使沒有可捲動的內容也一樣。

  • 如果只想裁剪,請勿使用 overflow: hidden,因為 hidden 會建立可透過程式輔助捲動的捲動容器。

.scrollable-list {
  max-block-size: 400px;
  overflow-y: auto;
  scrollbar-gutter: stable;       /* Reserve scrollbar space. */
  overscroll-behavior: contain;   /* No scroll chaining into the page. */
}

.snippet {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-clamp: 3;                  /* Ignored where unsupported. */
  overflow: clip;
}

如要深入瞭解 overflow: clip 和 overflow-clip-margin,請參閱「溢位裁剪控制項」。如要在滑動顯示 UI 中使用 overscroll-behavior: chain 進行巢狀捲動交接,請參閱「滑動顯示」。如要瞭解捲軸顏色、大小和主題,請參閱「自訂捲軸的顏色或粗細」、「深色模式」和「根據高對比偏好設定調整捲軸」。

7 檢視區機制和追蹤分布

  • 如果行動版版面配置容器必須考量瀏覽器 UI 移位 (網址列收合/展開),請使用 dvh/dvw。
  • 請勿使用 100vw 設定全寬版面配置,因為這會忽略捲軸寬度,導致水平溢位。請改用 100%、100dvw 或 100svw。

如需回應式設計的進入點 (動態可視區域單位、保留媒體空間、流動字體排版),請參閱「回應式設計」。

8 格線道 (又稱拼磚)

這項規格目前以「格線車道」為名開發中 (例如 display: grid-lanes),而先前的實驗原型則使用 grid-template-rows: masonry。

建議做法:

  • 立即使用格線搭配 grid-auto-flow: dense 進行緊密排列,並接受 DOM 順序可能與視覺順序不符。
  • 如果項目是文件片段,而非權重相同的資訊卡,請使用多欄 (columns: 3; column-gap: 1rem) 呈現內容豐富的磚石樣式流程。
  • 請將 grid-template-rows: masonry 視為漸進增強功能,並使用 @supports 偵測功能。

請勿:

  • 在基準目標趕上之前,請勿將 grid-template-rows: masonry 設為硬性規定。
.gallery       { columns: 3 200px; column-gap: 1rem; }
.gallery > *   { break-inside: avoid; margin-block-end: 1rem; }

@supports (grid-template-rows: masonry) {
  .gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    grid-template-rows: masonry;
    gap: 1rem;
    columns: unset;
  }
}