請啟用 Javascript 查看內容

CSS Grid

 ·  ☕ 8 分鐘

前言

建構網頁佈局是製作網站的基礎,早期排版是許多開發者的惡夢,但隨著 Flex 如毒品般的出現,用過就回不去了。許多前端框架使用 Flex 實現 Grid system,例如 Bootstrap。但基於 Flex 的 Grid system 靈活度有時候還是不太夠,而且遇到雙向排版時,會使用大量的巢狀結構。

CSS Grid 的出現,可以說是 CSS 真正的 Grid system,為目前 CSS 唯一的二維佈局。

瀏覽器支援

主流瀏覽基本上都支持,詳細支援情況可以查閱 can I use

基本概念

  • 網格容器 Grid container,網格系統的外層框架。
  • 網格項目 Grid item,網格容器下的直接子元素。
  • 網格線 Grid line,組成網格的分割線。
  • 網格軌道 Grid track,兩條網格線之間的區域,稱為網格欄 Grid column 或網格列 Grid row。
  • 網格單元格 Grid cell,網格的最小的面積單位。
  • 網格區塊 Grid area,由四條網格線所組成的矩形區域。

建立容器

使用 CSS Grid 的必要屬性。

1
<div class="container"></div>
1
2
3
.container {
  display: grid | inline-grid;
}

宣告以後,該元素會變成 grid container,所有直接子元素會變成 grid item。

畫格子

接下來我們來畫格子,也就是定義出基礎的 grid cell,我們可以使用 grid-template-rowsgrid-template-columns 來放置網格內的 grid line,這樣就產生垂直與水平的 grid track,也就是欄與列,兩者交叉就會形成一格一格的 grid cell。

1
2
3
4
5
6
7
.container {
  grid-template-rows: <track-size>...;
  grid-template-columns: <track-size>...;
  
  /* 簡寫 rows / columns */
  grid-template: <track-size>... / <track-size>...;
}

track-size 指的是 grid line 之間的距離,也就是 grid track 的尺寸。

舉例來說:

1
2
3
4
.container {
  grid-template-rows: 120px 180px;
  grid-template-columns: 120px 120px 60px;
}

上面程式碼的效果如下,2 列 3 欄會形成 6 格 grid cell:

我們可以開啟 Chrome Devtool 查看:

接著放入 grid item:

1
2
3
4
.item {
  border-radius: 12px;
  border: 2px solid #222;
}
1
2
3
4
5
6
7
8
<div class="container">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

預設的情況下,grid item 會各佔一格 grid cell。

1. fr 單位

如果 grid container 有定義寬高:

1
2
3
4
5
.container {
  width: 300px;
  height: 300px;
  display: grid;
}

我們可以使用 CSS Grid 專用單位 fr(fraction) 依照比例分配剩餘空間:

1
2
3
4
.container {
  grid-template-rows: 100px 1fr;
  grid-template-columns: 1fr 3fr;
}

使用 % 需要手動計算,而 fr 只需要設置幾等份就好,非常方便。

2. repeat() 函式

若尺寸值相同,可以使用 repeat() 表示重複的值:

1
2
3
4
5
.container {
  grid-template-rows: repeat(3, 1fr);
  /* 等同 */
  grid-template-rows: 1fr 1fr 1fr;
}
1
2
3
4
5
.container {
  grid-template-rows: 10px repeat(3, 1fr) 10px;
  /* 等同 */
  grid-template-rows: 10px 1fr 1fr 1fr 10px;
}
1
2
3
4
5
.container {
  grid-template-rows: repeat(3, 1fr 2fr);
  /* 等同 */
  grid-template-rows: 1fr 2fr 1fr 2fr 1fr 2fr;
}
1
2
3
4
5
.container {
  grid-template-rows: repeat(2, 1fr) 10px repeat(2, 2fr);
  /* 等同 */
  grid-template-rows: 1fr 1fr 10px 2fr 2fr;
}

3. 最大最小值

如果我們的 grid container 會隨著視窗做變化,當你使用 frauto 作為 track-size 大小時,要避免 grid track 太大或太小,可以使用 minmax() 函式可以定義最小最大值,。

舉例來說,欄寬最小值 auto、最大值不超過 150px

1
2
3
4
5
6
.container {
  width: 100%;
  display: grid;
  grid-template-rows: 60px;
  grid-template-columns: minmax(auto, 150px) 100px 50px;
}

4. auto-fill、auto-fit

使用 repeat() 時,若不知道要重複的數量,可以用 auto-fillauto-fit 自動將 grid track 填滿容器。

兩者差異在於,auto-fit 沒有用到的 grid track 會被設為 0px

1
2
3
<div class="container auto-fill"></div>

<div class="container auto-fit"></div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.container {
  background: #eee;
  width: 200px;
  display: grid;
  grid-template-rows: repeat(4, 50px);
}

.auto-fill {
  grid-template-columns: repeat(auto-fill, 50px);
}

.auto-fit {
  grid-template-columns: repeat(auto-fit, 50px);
}

接下來我們放入 grid item,兩者基本上沒什麼差別。

但如果我們將欄的 track-size 改成 minmax(50px, 1fr) 就會有明顯的差異。

放置網格項目

預設的情況下,grid item 各佔一格 grid cell。不過我們能分別定義 grid item 要佔的空間,也就是 grid area。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.item {
  grid-row-start: <name>;
  grid-row-end: <name>;
  
  grid-column-start: <name>;
  grid-column-end: <name>;

  /* 簡寫 start / end */
  grid-row: <name> / <name>;
  grid-column: <name> / <name>;
  
  /* 簡寫 row start / column start / row end / column end*/
  grid-area: <name> / <name> / <name> / <name>;
}

startend 分別表示 起始結束 的grid line。

Grid line 是有名稱的,預設為數字編號,由左至右(由上至下)為正值,由右至左(由下至上)則為負值。

舉例來說:

1
2
3
4
5
6
.item {
  grid-row-start: 2;
  grid-row-end: 5;
  grid-column-start: 2;
  grid-column-end: 4;
}

1. 跨度 span

除了指定 grid line 名稱,也可以使用 跨度 span <number> 表示要跨幾格 grid cell。

舉例來說,從第 5 條水平線往上跨 2 格、從第 2 條垂直線往左跨 3 格:

1
2
3
4
5
6
.item {
  grid-row-start: span 2;
  grid-row-end: 5;
  grid-column-start: 2;
  grid-column-end: span 3;
}

2. 自訂義名稱

除了預設編號,grid line 能使用自訂義名稱:

1
2
3
4
5
6
7
.container {
  grid-template-rows: [name] <track-size> [name]...;
  grid-template-columns: [name] <track-size> [name]...;
  
  /* 簡寫 rows / columns */
  grid-template: [name] <track-size> [name]... / [name] <track-size> [name]...;
}

舉例來說:

1
2
3
4
5
6
7
8
9
.container {
  grid-template-rows: [y1] 1fr [y2] 1fr [y3] 1fr [y4] 1fr [y5] 1fr [y6];
  grid-template-columns: [x1] 1fr [x2] 1fr [x3] 1fr [x4] 1fr [x5] 1fr [x6];
}

.item {
  grid-row: y1 / y4;
  grid-column: x1 / x5;
}

3. 順序

當我們手動放置 grid item 時,它們的 grid area 可能會有重疊的部分,若要改變顯示順序,可以使用 z-index 分層或 order 排序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
.item-1 {
  background: rgba(255, 0, 0, 0.8);
  grid-row: 1/ 4;
  grid-column: 1 / 5;
  z-index: 2;
}

.item-2 {
  background: rgba(0, 255, 0, 0.8);
  grid-row: 3 / 5;
  grid-column: 2 / 6;
  z-index: 3;
}

.item-3 {
  background: rgba(0, 0, 255, 0.8);
  grid-row: 2 / 6;
  grid-column: 1 / 3;
  z-index: 1;
}

4. auto

預設的情況下,grid item 各佔一格 grid cell,因為 grid-area 的預設值為 auto,grid item 會被以預設跨度 1 自動放置。

auto 會自動尋找下一條可用的 grid line,如果要將 grid item 自動跨 2 欄 grid item 不重疊,只需要設置 grid-column-end

1
2
3
4
.item {
  grid-column-start: auto: /* 預設值 auto */
  grid-column-end: span 2;
}

5. 區塊

我們還可以使用 grid-template-areas 定義 grid area,再利用 grid-area 對應到相對的名稱。

1
2
3
.container {
  grid-template-areas: <string>;
}

每個字串都代表一列,使用要定義的 grid area 名稱圍成矩形,沒有要使用到的 grid cell 可以使用任何字元跳過。

舉例來說:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
.container {
  /* ... */
  grid-template-areas:
    'xx xx . .  .'
    'xx xx . yy yy'
    '.  .  . yy yy' 
    'zz zz . yy yy'
    'zz zz . .  .';
}

.item-1 {
  grid-area: x;
}
.item-2 {
  grid-area: y;
}
.item-3 {
  grid-area: z;
}

注意,定義 grid area 只能是矩形的範圍,非矩形為無效宣告。

間距

Grid area 之間的 間距(gutter)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.container {
  /* 標準 */
  row-gap: <line-size>;
  column-gap: <line-size>;
  /* 簡寫 row column */
  gap: line-size> | <line-size> <line-size>;


  /* 舊語法 */
  grid-row-gap: <line-size>;
  grid-column-gap: <line-size>;
  /* 簡寫 row column */
  grid-gap: <line-size> | <line-size> <line-size>;
}

line-size 只能使用長度值或百分比。

對齊

1. content

當網格環境小於 grid content 時,可以使用以下屬性對齊。

justify-content 水平對齊、align-content 垂直對齊:

  • start:靠左對齊。
  • end:靠右對齊。
  • center:靠中間對齊
  • stretch:依照比例拉寬到滿版。
  • space-around:在各格線間放入多餘的空白,包含開頭和結尾。
  • space-between:只在各格線間放入多餘的空白。
  • space-evenly:效果同 space-around 但開頭和結尾的空白較多。

place-content 是兩者的簡寫,align-content 為第一個值、justify-content 為第二值,若兩者相等,可以只設置一個值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-rows: 50px 50px 50px;
  grid-template-columns: 50px 50px 50px;
  
  justify-content: space-between;
  align-content: space-between;
  
}

2. items

預設的情況下,我們的 grid item 大小會等於 grid area。但如果 grid item 有定義尺寸,小於 grid area 時,可以使用以下屬性對齊。

justify-items 水平對齊、align-items 垂直對齊:

  • start:靠左對齊。
  • end:靠右對齊。
  • center:靠中間對齊
  • stretch:依照比例拉寬到滿版。

place-items 則是兩者的簡寫,align-items 為第一個值、justify-items 為第二值,若兩者相等,可以只設置一個值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
.container {
  width: 300px;
  height: 300px;
  display: grid;
  grid-template-rows: 100px 100px 100px;
  grid-template-columns: 100px 100px 100px;
  
  justify-items: center;
  align-items: center;
}

.item {
  width: 50px;
  height: 50px;
}

3. self

如果你的 grid item 要單獨對齊,可以在 grid item 中使用以下屬性對齊。

justify-self 水平對齊、align-self 垂直對齊:

  • start:靠左對齊。
  • end:靠右對齊。
  • center:靠中間對齊
  • stretch:依照比例拉寬到滿版。

place-self 則是兩者的簡寫,align-self 為第一個值、justify-self 為第二值,若兩者相等,可以只設置一個值。

隱式網格

在 grid cell 有限的情況下,如果 grid item 過多,就會自動增加 grid track,也就是 隱式網格

1. 隱式網格的大小

隱式網格的 grid track 預測為自動大小,但可以透過 grid-auto-rowsgrid-auto-columns 定義。

1
2
3
4
.container{
  grid-auto-rows: <track-size> ...;
  grid-auto-columns: <track-size> ...;
}

舉例來說,我們只定義 2 欄 1 列:

1
2
3
4
5
6
.container {
  grid-template-rows: 60px;
  grid-template-columns: repeat(2, 60px);

  grid-auto-rows: 120px;
}

如果我們放置 4 個 grid track,會自動新增第 2 列 grid track。

網格流向

預設的情況下,網格流向為 row,也就是 grid item 自動放置時,會水平往右排列。

我們可以使用 grid-auto-flow 改變網格的流向:

1
2
3
.container {
  grid-auto-flow: row | column | row dense | column dense;
}

舉例來說,將流向改成 column 垂直向下:

1
2
3
4
5
6
.container {
  display: grid;
  grid-template-rows: repeat(3, 60px);
  grid-template-columns: repeat(3, 60px);
  grid-auto-flow: column;
}

若加上 dense,會嘗試將 grid track 填滿,因此可能會改變 grid item 順序,要特別留意。

1
2
3
4
5
6
<div class="container">
  <div class="item item-2"></div>
  <div class="item item-2"></div>
  <div class="item item-1"></div>
  <div class="item item-1"></div>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
.container {
  display: grid;
  grid-template-rows: repeat(3, 60px);
  grid-template-columns: repeat(3, 60px);
  grid-auto-flow: row dense;
}

.item-1 {
  grid-column-end: span 1;
}

.item-2 {
  grid-column-end: span 2;
}

預設的情況下,第 1 列會有空隙,但使用 row dense 後,會將後面較小的 item-1 往前填滿。

佈局範例

1. 聖杯佈局

1
2
3
4
5
6
7
<div class="container">
  <header>Header</header>
  <aside>Aside</aside>
  <main>Main</main>
  <aside>Aside</aside>
  <footer>Footer</footer>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.container {
  width: 100%;
  min-width: 600px;
  display: grid;
  grid-template-columns: 100px auto 100px;
  gap: 10px;
}

header, footer {
  grid-column: 1 / 4;
}

移動端響應式佈局,可以加上媒體查詢:

1
2
3
4
5
@media all and (max-width: 700px) {
  aside, main {
    grid-column: 1 / 4;
  }
}

2. 960 grid system


竹白
作者
竹白
前端筆記

文章目錄