使用網格實現常見佈局

為了總結這篇CSS 網格佈局指南,我們將介紹幾種不同的佈局,展示一些在使用網格佈局進行設計時可以使用的技巧。我們將看一個使用 grid-template-areas 的示例,一個 12 列的彈性網格系統,以及一個使用自動放置的產品列表。正如你從這些示例中看到的,使用 CSS 網格佈局通常有不止一種方法來獲得你想要的結果。請選擇對你正在解決的問題和需要實現的設計最有幫助的方法。

使用 grid-template-areas 實現 1 到 3 列的響應式流體佈局

許多網站都採用這類佈局的變體,包含內容區、側邊欄、頁首和頁尾。在響應式設計中,你可能希望將佈局顯示為單列,在某個斷點處新增一個側邊欄,然後為更寬的螢幕引入三列布局。

three different layouts created by redefining the grid at two breakpoints.

我們將使用在網格模板區域指南中學到的命名模板區域來建立這個佈局。

HTML 結構是一個容器,內部包含頁首、頁尾、主內容、導航、側邊欄以及一個用於放置廣告的塊級元素。

html
<div class="wrapper">
  <header class="main-head">The header</header>
  <nav class="main-nav">
    <ul>
      <li><a href="">Nav 1</a></li>
      <li><a href="">Nav 2</a></li>
      <li><a href="">Nav 3</a></li>
    </ul>
  </nav>
  <article class="content">
    <h1>Main article area</h1>
    <p>
      In this layout, we display the areas in source order for any screen less
      that 500 pixels wide. We go to a two column layout, and then to a three
      column layout by redefining the grid, and the placement of items on the
      grid.
    </p>
  </article>
  <aside class="side">Sidebar</aside>
  <div class="ad">Advertising</div>
  <footer class="main-footer">The footer</footer>
</div>

由於我們使用 grid-template-areas 來建立佈局,我們需要在任何媒體查詢之外為區域命名。我們使用 grid-area 屬性來命名區域。

css
.main-head {
  grid-area: header;
}
.content {
  grid-area: content;
}
.main-nav {
  grid-area: nav;
}
.side {
  grid-area: sidebar;
}
.ad {
  grid-area: ad;
}
.main-footer {
  grid-area: footer;
}

這並不會建立佈局。相反,專案現在有了名稱,我們可以用這些名稱來建立佈局。在任何媒體查詢之外,我們現在要為移動裝置寬度設定佈局。這裡,我們將所有內容保持在源順序中,以避免源順序和顯示順序之間出現任何脫節,正如網格佈局與無障礙指南中所述。我們沒有顯式定義任何列或行軌道;這個佈局決定了單列,並根據隱式網格中每個專案的需要建立行。

css
.wrapper {
  display: grid;
  gap: 20px;
  grid-template-areas:
    "header"
    "nav"
    "content"
    "sidebar"
    "ad"
    "footer";
}

在移動端佈局就位後,我們現在可以繼續新增一個 @media 查詢,以便將此佈局調整到具有足夠空間顯示兩列的更大螢幕上。

css
@media (width >= 500px) {
  .wrapper {
    grid-template-columns: 1fr 3fr;
    grid-template-areas:
      "header  header"
      "nav     nav"
      "sidebar content"
      "ad      footer";
  }
  nav ul {
    display: flex;
    justify-content: space-between;
  }
}

你可以看到佈局在 grid-template-areas 的值中初具雛形。header 跨越兩個列軌道,nav 也是如此。在第三個行軌道中,我們將 sidebarcontent 並排放置。我們將 ad 內容放在第四個行軌道中,使其出現在側邊欄下方。footer 在它旁邊,位於內容下方。我們對導航使用 CSS 彈性盒佈局,以便在一行中均勻地分佈導航專案。

我們現在可以為能夠顯示三列布局的更寬螢幕新增最後一個斷點。

css
@media (width >= 700px) {
  .wrapper {
    grid-template-columns: 1fr 4fr 1fr;
    grid-template-areas:
      "header header  header"
      "nav    content sidebar"
      "nav    content ad"
      "footer footer  footer";
  }
  nav ul {
    flex-direction: column;
  }
}

三列布局有兩個 1fr 單位的側邊欄列和一箇中間列,其軌道大小為 4fr。這意味著容器中的可用空間被分成六份,並按比例分配給我們的三個軌道——側邊欄各佔一份,中間列佔四份。

在這個佈局中,我們將 nav 顯示在左列,與 content 並排。右列有 sidebar,其下方是廣告(ad)。footer 現在跨越了整個佈局的底部。我們再次使用 Flexbox 來顯示導航,但這次我們將其顯示為一列而不是一行。

這個基本示例演示瞭如何在不同斷點之間重新排列網格佈局。特別是,我們根據不同的列設定,適當地更改了 ad 塊的位置。這種命名區域的方法非常有用,尤其是在原型設計階段。你可能會發現在調整網格上元素位置時,使用名稱比使用數字更容易。

一個靈活的 12 列布局

CSS 框架和網格系統通常使用 12 列或 16 列的彈性網格。我們可以使用 CSS 網格佈局來建立這種型別的系統。例如,讓我們建立一個 12 列的彈性網格,其中有 12 個 1fr 單位的列軌道,每個軌道的起始線都命名為 col-start。這意味著我們會有十二條名為 col-start 的網格線。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
  gap: 20px;
}

為了演示這個網格系統的工作原理,我們在一個包裝器中有四個子元素。

html
<div class="wrapper">
  <div class="item1">Start column line 1, span 3 column tracks.</div>
  <div class="item2">
    Start column line 6, span 4 column tracks. 2 row tracks.
  </div>
  <div class="item3">Start row 2 column line 2, span 2 column tracks.</div>
  <div class="item4">
    Start at column line 3, span to the end of the grid (-1).
  </div>
</div>

然後,我們可以使用命名線以及 span 關鍵字將它們放置在網格上。

css
.item1 {
  grid-column: col-start / span 3;
}
.item2 {
  grid-column: col-start 6 / span 4;
  grid-row: 1 / 3;
}
.item3 {
  grid-column: col-start 2 / span 2;
  grid-row: 2;
}
.item4 {
  grid-column: col-start 3 / -1;
  grid-row: 3;
}

使用命名網格線指南中所述,我們正在使用命名線來放置我們的專案。由於我們有 12 條同名的線,我們使用名稱和線的索引。如果你願意,你可以使用線索引本身,而避免使用命名線。

我們沒有設定結束線的編號,而是使用 span 關鍵字定義該元素應跨越多少個軌道。在使用多列布局系統時,對於那些習慣於根據網格軌道跨越數量來考慮塊,然後根據不同斷點進行調整的人來說,這種方法可能更直觀。要檢視塊如何與軌道對齊,請使用瀏覽器開發者工具中的網格檢查器;它可能會清楚地展示專案是如何放置的。

Showing the items placed on the grid with grid tracks highlighted in Firefox developer tools.

我們不需要新增任何標記來建立行。CSS 框架網格系統通常這樣做是為了防止在不支援 CSS 網格佈局的瀏覽器中,元素會跳到上一行。然而,這一點現在已經沒有意義了——所有現代瀏覽器都早已支援 CSS 網格佈局。CSS 網格允許我們將專案放置到行中,即使上一行是空的,也不存在它們會上升到上一行的風險。由於這種嚴格的列和行放置,我們也可以輕鬆地在佈局中留下空白。我們也不需要特殊的類來將專案縮排到網格中。我們所需要做的就是指定專案的起始和結束線。

使用 12 列系統構建佈局

為了瞭解這種佈局方法在實踐中是如何工作的,我們可以建立與使用 grid-template-areas 建立的相同佈局,這次使用 12 列網格系統。讓我們從網格模板區域示例中使用的相同標記開始。

html
<div class="wrapper">
  <header class="main-head">The header</header>
  <nav class="main-nav">
    <ul>
      <li><a href="">Nav 1</a></li>
      <li><a href="">Nav 2</a></li>
      <li><a href="">Nav 3</a></li>
    </ul>
  </nav>
  <article class="content">
    <h1>Main article area</h1>
    <p>
      In this layout, we display the areas in source order for any screen less
      that 500 pixels wide. We go to a two column layout, and then to a three
      column layout by redefining the grid, and the placement of items on the
      grid.
    </p>
  </article>
  <aside class="side">Sidebar</aside>
  <div class="ad">Advertising</div>
  <footer class="main-footer">The footer</footer>
</div>

我們像上面的 12 列布局示例一樣設定我們的網格。

css
.wrapper {
  display: grid;
  grid-template-columns: repeat(12, [col-start] 1fr);
  gap: 20px;
}

我們將再次使其成為一個響應式佈局,這次使用命名線。每個斷點都將使用一個 12 列的網格。然而,專案將跨越的軌道數量將根據螢幕的大小而改變。

我們從移動優先開始。對於最窄的螢幕,我們希望專案保持源順序並全部跨越整個網格。

css
.wrapper > * {
  grid-column: col-start / span 12;
}

在下一個斷點,我們想要一個兩列布局。我們的頁首和導航仍然跨越整個網格,所以我們不需要為它們指定任何定位。側邊欄從名為 col-start 的第一條列線開始,跨越 3 條線。它位於第 3 條行線之後,因為頁首和導航在前兩個行軌道中。

ad 面板位於側邊欄下方,從網格第 4 行線開始。然後我們有內容和頁尾,從 col-start 4 開始並跨越九個軌道,將兩者都帶到網格的末尾。

css
@media (width >= 500px) {
  .side {
    grid-column: col-start / span 3;
    grid-row: 3;
  }
  .ad {
    grid-column: col-start / span 3;
    grid-row: 4;
  }
  .content,
  .main-footer {
    grid-column: col-start 4 / span 9;
  }
  nav ul {
    display: flex;
    justify-content: space-between;
  }
}

最後,對於大於我們最大斷點的螢幕,我們定義此佈局的三列版本。頁首繼續跨越整個網格,但現在導航下移成為第一個側邊欄,旁邊是內容,然後是側邊欄。頁尾現在也跨越整個佈局。

css
@media (width >= 700px) {
  .main-nav {
    grid-column: col-start / span 2;
    grid-row: 2 / 4;
  }
  .content {
    grid-column: col-start 3 / span 8;
    grid-row: 2 / 4;
  }
  .side {
    grid-column: col-start 11 / span 2;
    grid-row: 2;
  }
  .ad {
    grid-column: col-start 11 / span 2;
    grid-row: 3;
  }
  .main-footer {
    grid-column: col-start / span 12;
  }
  nav ul {
    flex-direction: column;
  }
}

再次檢查瀏覽器開發者工具中的網格檢查器,看看佈局是如何形成的。

Showing the layout with grid tracks highlighted by the grid inspector.

在我們建立這個佈局時需要注意的一點是,我們不需要在每個斷點顯式地定位網格上的每個元素。我們繼承了為早期斷點設定的佈局——這是“移動優先”工作方式的一個優勢。我們還利用了網格的自動放置功能。透過保持元素的邏輯順序,自動放置為我們在網格上放置專案做了很多工作。

使用自動放置的產品列表

在本指南的最後一個示例中,我們建立了一個完全依賴於自動放置的佈局。

許多佈局本質上是“卡片”的集合——產品列表、圖片庫等等。網格使得建立這些列表的方式能夠響應式,而無需新增媒體查詢。在此示例中,我們結合了 CSS 網格和 Flexbox 佈局來製作一個基本的產品列表佈局。

列表的標記是一個無序的專案列表。每個專案包含一個標題、一些不同高度的文字和一個號召性用語連結。

html
<ul class="listing">
  <li>
    <h2>Item One</h2>
    <div class="body">
      <p>The content of this listing item goes here.</p>
    </div>
    <div class="cta">
      <a href="">Call to action!</a>
    </div>
  </li>
  <li>
    <h2>Item Two</h2>
    <div class="body">
      <p>The content of this listing item goes here.</p>
    </div>
    <div class="cta">
      <a href="">Call to action!</a>
    </div>
  </li>
  <li class="wide">
    <h2>Item Three</h2>
    <div class="body">
      <p>The content of this listing item goes here.</p>
      <p>This one has more text than the other items.</p>
      <p>Quite a lot more</p>
      <p>Perhaps we could do something different with it?</p>
    </div>
    <div class="cta">
      <a href="">Call to action!</a>
    </div>
  </li>
  <li>
    <h2>Item Four</h2>
    <div class="body">
      <p>The content of this listing item goes here.</p>
    </div>
    <div class="cta">
      <a href="">Call to action!</a>
    </div>
  </li>
  <li>
    <h2>Item Five</h2>
    <div class="body">
      <p>The content of this listing item goes here.</p>
    </div>
    <div class="cta">
      <a href="">Call to action!</a>
    </div>
  </li>
</ul>

我們建立一個具有靈活數量的彈性列的網格。我們希望它們至少有 200 畫素寬,並平均共享任何可用的剩餘空間——這樣我們總是能得到等寬的列軌道。我們透過在軌道大小的 repeat() 表示法中使用 minmax() 函式來實現這一點。

css
.listing {
  list-style: none;
  margin: 2em;
  display: grid;
  gap: 20px;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}

當我們新增此 CSS 時,專案將被佈局為網格。如果我們將視窗變小或變寬,列軌道的數量會發生變化——無需媒體查詢新增斷點,也無需重新定義網格。

我們可以使用一點 Flexbox 來整理盒子的內部。我們將列表項設定為 display: flex,並將 flex-direction 設定為 column。然後我們可以對 .cta 使用 auto margin 將這個欄推到盒子的底部。

css
.listing li {
  border: 1px solid #ffe066;
  border-radius: 5px;
  display: flex;
  flex-direction: column;
}
.listing .cta {
  margin-block-start: auto;
  border-block-start: 1px solid #ffe066;
  padding: 10px;
  text-align: center;
}
.listing .body {
  padding: 10px;
}

這是使用 Flexbox 而不是 CSS 網格佈局的關鍵原因之一。如果你正在單個維度上對齊或分佈內容,那就是 Flexbox 的用例。

使用 dense 關鍵字防止間隙

這看起來已經相當完整了。然而,我們有時會有一些卡片包含比其他卡片多得多的內容。讓那些卡片跨越兩個軌道可能會很好,這樣它們就不會那麼高。我們在較大的專案上新增一個 wide 類,並新增一條規則,給它一個值為 span 2grid-column-end。當遇到此專案時,它將被分配到兩個軌道。這意味著,在某些斷點,我們會在網格中得到一個間隙——那裡沒有足夠的空間來佈置一個雙軌道的專案。

The layout has gaps as there is not space to lay out a two track item.

我們可以透過在網格容器上設定 grid-auto-flow: dense 來讓網格回填這些間隙。這樣做時要小心,因為它可能導致專案脫離其邏輯源順序。只有當你的專案沒有固定順序時才應該這樣做。此外,請注意由於 Tab 鍵順序遵循源順序而非重新排序後的顯示順序所導致的無障礙和重新排序問題

css
.listing {
  list-style: none;
  margin: 2em;
  display: grid;
  gap: 20px;
  grid-auto-flow: dense;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.listing .wide {
  grid-column-end: span 2;
}

使用自動放置並對某些專案應用一些規則非常有用,並且可以幫助處理你無法控制的內容,例如 CMS 輸出,其中你有重複的專案,並且可以使用結構偽類來定位它們。

進一步探索

CSS 網格佈局提供瞭如此多的可能性。學習使用網格佈局的最佳方法是繼續構建像我們這裡所介紹的示例。從你喜歡的響應式網站中挑選一個佈局,看看你是否可以使用網格來構建它。你甚至可以從雜誌或其他非 Web 來源中獲取靈感。