CSS 排版像煮咖啡:杯子(HTML)先放好,比例(CSS)調對,整杯才不會溢出。這篇會用一個儀表板範例,帶你從 HTML 結構、grid-template-areas 到響應式版面,逐步做出桌面與手機都能閱讀的 CSS Grid 版面;不用把每個區塊都 position: absolute,否則最後可能連咖啡都要用座標找。

讀完後,你會知道如何把頁面切成具名區域、在主要內容裡建立巢狀網格、用 fr 分配剩餘空間,還能用媒體查詢切換成單欄版面。先把棋盤畫出來,接下來每個區塊就有自己的座位。

建立 HTML 結構

先準備兩個檔案:index.html 放內容,styles.css 放樣式。HTML 不需要知道每個區塊最後要站在哪一格,只要用有意義的 class 告訴 CSS「我是誰」即可。

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>CSS Grid 儀表板</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <div class="grid-container">
    <header class="header">網站標題</header>
    <nav class="top-nav">頂部導覽</nav>
    <aside class="sidebar">側邊欄</aside>
    <main class="main-content">
      <div class="widget widget-1">統計卡片 1</div>
      <div class="widget widget-2">統計卡片 2</div>
      <div class="widget widget-3">統計卡片 3</div>
      <div class="widget widget-4">統計卡片 4</div>
    </main>
    <section class="extra-content">額外資訊</section>
    <section class="notifications">通知</section>
    <footer class="footer">頁尾</footer>
  </div>
</body>
</html>

這裡的 headernavasidemainsectionfooter 也讓文件結構更容易理解。接著,讓最外層的 .grid-container 變成網格容器。

定義主要網格

display: grid 會把元素變成 Grid 容器;直接子元素則會成為 Grid 項目。grid-template-columns 定義欄,grid-template-rows 定義列,而 fr 代表可分配的剩餘空間。

這個範例用三欄:左右欄各佔 0.5fr,中央主要內容佔 2fr。因此中央欄會比左右欄寬,但實際寬度仍會隨容器變化。

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: system-ui, sans-serif;
}

.grid-container {
  display: grid;
  grid-template-columns: 0.5fr 2fr 0.5fr;
  grid-template-rows: auto auto minmax(300px, 1fr) auto auto;
  grid-template-areas:
    "header header header"
    "top-nav top-nav top-nav"
    "sidebar main-content extra-content"
    "sidebar notifications extra-content"
    "footer footer footer";
  gap: 10px;
  min-height: 100vh;
  padding: 10px;
}

這裡使用 min-height: 100vh,讓內容少時至少填滿視窗,但不會像固定 height 那樣在內容變多時硬把內容塞出容器。版面不是行李箱,不需要為了合蓋而壓扁內容。

grid-template-areas 是這個範例的重點。每一行代表一列,每個名稱代表一個網格儲存格;相同名稱連在一起,就會形成同一個區域。名稱的數量要對齊,否則整個宣告會變成無效 CSS。

放置標頭與導覽列

接著用 grid-area 把 HTML 元素接到剛才命名的區域。grid-area: header 的意思不是「請瀏覽器自由發揮」,而是「把我放到名為 header 的區域」。

.header {
  grid-area: header;
  padding: 16px;
  color: #fff;
  background-color: #388e3c;
  text-align: center;
}

.top-nav {
  grid-area: top-nav;
  padding: 12px;
  color: #fff;
  background-color: #1976d2;
  text-align: center;
}

樣式本身可以依專案設計調整;Grid 關心的是位置,不會限制你的顏色、字型或圓角。換句話說,它是排版工具,不是來管你喝美式還是拿鐵的。

設定左右側欄

側欄與額外資訊分別佔據左右兩側。指定 grid-area 後,這些元素就會按照模板排列,不需要為每個元素手動計算欄線編號。

.sidebar {
  grid-area: sidebar;
  padding: 16px;
  background-color: #ffc107;
}

.extra-content {
  grid-area: extra-content;
  padding: 16px;
  background-color: #009688;
}

這種寫法的優點是「版面長相」直接寫在容器裡。日後要調整配置時,通常只需修改 grid-template-areas,不用在好幾個選擇器裡追蹤 grid-columngrid-row

在主要內容中建立巢狀網格

Grid 容器可以放在另一個 Grid 項目裡。.main-content 先透過 grid-area 到外層的主要區域,再自己設定 display: grid,成為內層的網格容器。

.main-content {
  display: grid;
  grid-area: main-content;
  grid-template-columns: 1fr 2fr;
  grid-template-rows: repeat(2, minmax(120px, auto));
  gap: 10px;
  padding: 10px;
  background-color: #f5f5f5;
}

.widget {
  display: grid;
  place-items: center;
  min-height: 120px;
  padding: 16px;
  color: #fff;
  text-align: center;
}

.widget-1 {
  background-color: #e91e63;
}

.widget-2 {
  background-color: #9c27b0;
}

.widget-3 {
  background-color: #673ab7;
}

.widget-4 {
  background-color: #3f51b5;
}

repeat(2, minmax(120px, auto)) 代表建立兩列,每列至少 120px,內容需要更多空間時可以增加高度。gap 則控制網格項目之間的間距,比替每個卡片加一堆 margin 更容易集中管理。

完成通知與頁尾

最後處理通知區域與頁尾。它們的位置已經在 grid-template-areas 裡定義好,現在只要補上外觀即可。

.notifications {
  grid-area: notifications;
  padding: 12px;
  background-color: #8bc34a;
  text-align: center;
}

.footer {
  grid-area: footer;
  padding: 12px;
  color: #fff;
  background-color: #795548;
  text-align: center;
}

目前桌面版的版面已經完成。你可以開啟原本的實際範例查看效果:CSS Grid 儀表板範例。接著把視窗縮窄,會看到三欄開始變得擁擠;這就是響應式設計該登場的時候。

桌面版效果。

加上響應式設計

媒體查詢可以依據視窗寬度套用不同樣式。這裡在寬度小於或等於 768px 時,把容器改成單欄,並重新排列具名區域。

@media (max-width: 768px) {
  .grid-container {
    grid-template-columns: 1fr;
    grid-template-rows: auto;
    grid-template-areas:
      "header"
      "top-nav"
      "sidebar"
      "main-content"
      "extra-content"
      "notifications"
      "footer";
  }

  .main-content {
    grid-template-columns: 1fr;
  }
}

注意,改成單欄時,grid-template-areas 的每一列都只有一個名稱,而且順序就是手機上由上到下的順序。這比只把所有元素設成全寬更完整:你同時改了欄數,也清楚指定了內容順序。

內層 .main-content 也改成單欄,讓四張卡片在小螢幕上依序排列。若卡片內容較複雜,還可以再依實際需求調整間距與最小高度;斷點不是魔法數字,而是版面開始不好讀時的提醒。

手機版效果。

常見檢查方式

完成後,可以用瀏覽器的開發者工具檢查 .grid-container。多數現代瀏覽器都能顯示 Grid overlay,協助你確認欄線、區域和間距是否符合預期。

如果區域沒有出現,先依序檢查:

  1. 容器是否有 display: grid
  2. grid-template-areas 每一列是否有相同數量的儲存格。
  3. 子元素的 grid-area 名稱是否和模板完全一致。
  4. 每個具名區域是否由矩形儲存格組成;不能拼出 L 形區域。
  5. 媒體查詢內是否真的覆寫了桌面版的欄數與區域模板。

重點整理

這個儀表板範例用到的核心觀念有:

  • display: grid:建立 Grid 容器,讓直接子元素成為網格項目。
  • grid-template-columnsgrid-template-rows:定義欄與列的尺寸。
  • fr:按比例分配可用的剩餘空間。
  • grid-template-areas:用名稱描述整體版面,讓 CSS 讀起來像一張簡單的配置圖。
  • grid-area:把項目放進指定的具名區域。
  • 巢狀 Grid:在主要內容內再次建立網格,適合排列卡片或小元件。
  • gap:統一控制網格項目之間的間距。
  • @media:在不同視窗條件下切換欄數與區域配置。

CSS Grid 不會替你決定頁面內容,也不會自動判斷哪張卡片最重要;它負責把你描述的版面可靠地排出來。先從具名區域開始,等你熟悉後,再搭配 Grid line、auto-fitminmax() 做更彈性的元件版面。今天先把咖啡放在桌上,別放進網格裡——那會是另一種溢位問題。

參考資料