September 11, 2021

[CSS] Flex/Grid Layout Modules, part 6

[CSS] Flex/Grid Layout Modules, part 6
caniuse grid layout Level 1

你如果會用 Excel,那 Grid 就應該不陌生。

不過老實講,我也不太會用 Excel。

Grid Layout Module

其實他已經 CR 好一陣子了,目前多數主流的 CSS 框架已經開始採用。所以並不是你不會用或是沒有使用到這個東西,而是你已經在用了,但是你不知道他背後是使用 Grid Layout 而已。

caniuse grid layout Level 1

Grid 的基本結構就跟我剛剛提到的 Excel 很像,他是一個矩形區塊,包含了欄與列的設定。具體想要解決的問題在於「排版」這件事情,但試著回想這將近 20 年來排版的變化,是不是又很像回到了 <table> 的年代呢?


Grid 基本介紹

Grid Layout 基本上包含了下列結構:

  1. Grid 容器
  2. Grid 元件(或其子集合)
  3. 容器尺寸(寬度與高度)

相較之下,他沒有像是 Flexbox 有所謂的 軸方向 的問題。

Grid 容器

基本的 Grid 容器組成就是這樣,看起來很簡單。由於沒有 Flexbox 的主要軸、交叉軸的問題,所以操作起來相對會比 Flexbox 容易一點點。


Grid 容器

首先,宣告一個 Grid 容器一樣是使用 display 屬性,

  1. display: grid 亦即使用區塊(Box Level)來設定容器。
  2. display: inline-grid 亦即使用行內(Inline Level)來設定容器。

這裡跟 Flexbox 一樣,被定義出來的容器屬於 grid formatting context,所以原本在 Flexbox 上面會失效的事情,在這邊也一樣會失效。

  1. float, clear 會被忽略。
  2. vertical-align 無法應用在 Flex 元件上。
  3. ::first-line, ::first-letter 這兩個擬似元件無法套用至容器上。

同樣的,倘若元素指定 display: inline-grid 的話,在計算樣式值(Computed Value)會呈現 grid 而不是 inline-grid,這邊也是跟 Flexbox 一模一樣。

最大的不同點在於,Grid Layout 帶來了 格線軌道Grid track)的概念。

一個 Grid 容器依據橫向(row)與縱向(column)來分割,這個分割的動作就是一個 格線軌道Grid track)。這個軌道又可以區分為 網格單元Grid cell)與 網格格線Grid line)兩個面向。然後,網格單元與格線還可以區分出 命名單元(區域)命名格線 的差異。

在這裡先有個底就好,後面會大量提及這些事情。


容器尺寸與限制

Grid 容器本身可以設定一定尺寸,但若不指定容器尺寸的情況下,Grid 容器尺寸會依照這些項目的總和來決定外部尺寸,

  1. 每一個格線軌道尺寸
  2. 包含間隔(gap
  3. 每個軌道內容的最大(或最小)的內容尺寸(min-contentmax-content

這樣會決定出 Grid 的最小或最大尺寸該是多少。通常在 軌道尺寸 夠用的情況下都不會有太大問題。問題在於不夠用的情況,倘若真的不夠用,就會採用軌道尺寸下的 min-content 來當作軌道尺寸,你的 Grid 容器尺寸設定基本上會無效。我後面會繼續舉例,這邊就先有個概念即可。

另外,倘若裝置對於網格有其限制,那麼在網格容器內的網格格線就會被限制。

  1. 當你的網格元件跨度(span)超出網格格線限制,則會被限制在最後一個網格軌道上。
  2. 當你的網格元件完全超出網格格線限制,則會被限制在最後一個網格軌道上,且網格軌道永遠為 1。

關於尺寸限制其實不太容易遇到,除非你故意的,或是你真的在一些比較特殊的顯示裝置要使用的時候,才必須留意這件事情。


網格單元與格線

在 Grid 容器當中,被 grid-template-rowsgrid-template-columns 分割出來的區域,叫做網格單元(Grid cell),而這個容器單元會有所謂的格線,這個格線就是網格格線(Grid line)。

Grid area and Grid line

上圖用數字標出來的就是網格格線(Grid line),網格格線還有分成兩種:

  1. 一般網格格線(或命名網格格線)
  2. 隱性網格格線(implicit grid

關於 Implicit Grid 我後面會介紹,如果你想看官方解釋也可以:

The Implicit Grid

但我不保證看得懂。


容器樣式

這邊與 Flex 不同的點在於,他的樣式應用非常多,也相對複雜。

樣式 可用值 預設值
grid-template-columns none, <軌跡清單> none
grid-template-rows none, <軌跡清單> none
grid-template-areas none, <字串> none
grid-template 上述三個設定的簡寫 none
grid-auto-columns <軌跡尺寸> auto
grid-auto-rows <軌跡尺寸> auto
grid-auto-flow row, column, dense row
grid 上述全部的簡寫 none

以上是一個 Grid 容器可以設定,關於容器本身的樣式。接著我們來看看裡面的設定值到底寫了些什麼東西。


軌跡清單

我們在設定網格的時候,比較常見的作法會類似這樣,

.grid-container {
    display: grid;
    grid-template-columns: 100px 500px;
    grid-template-rows: 100px 100px 100px;
}

歐,你說我怎麼不用 fr 來當例子?這個新的單位後面會提到。這樣我們就可以擁有一個很簡單的網格容器,他包含了,

  1. 兩個欄(columns),寬度為 100px500px
  2. 三個行(_row),高度皆為 100px

這邊也可以搭 CSS 運算涵式或關鍵字使用,例如,

.grid-container {
    grid-template-columns: 100px repeat(50px, 3) minmax(min-content, 1vw);
}

另外,前述有提到了網格格線(Grid line),在這邊可以替你的格線命名,預設在不命名的情況下,都是以 數字 來定義網格格線,

.grid-container {
    grid-template-row: [first side-begin] 100px [side-ended main-begin] 100px [main-ended last];
}

格線名命名的規則就是用 [] 把他包起來,這樣你的格線就會有一個特別的名字。另外請留意,不要用 -start-end 來當作名稱,這樣的命名方式會踩到網格格線的雷。另外,名字可以一樣,但在指定的時候必須要特別指定你要在哪一個格線。

Grid 網格格線命名

當你使用命名格線時,在指定網格單元時就可以使用名稱,而不是去計算數字,相對來說會比較方便一些。但如果你把網格格線的名稱都設定成同一個名字,那你在取用的時候還是要指定對應的數字才行。

但,都要你取名字了,盡量還是不要一樣比較好。

至於 -start-end 的雷,後續提到隱性網格會繼續講。


grid-template-areas 的字串定義

這個樣式的使用方式比較特別,他是需要定義每一個 網格單元 的名字,也就是說,我們可以寫成類似這樣,

.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar main main"
        "sidebar main main";
}

所以,這個時候你的 HTML 就只會有三個元件,

<div class="grid-container">
    <nav class="nav">
    </nav>
    <aside class="sidebar">
    </aside>
    <main class="main">
    </main>
</div>

而這三個元件搭配的設定會是這樣,

.nav {
    grid-area: nav;
}

.sidebar {
    grid-area: sidebar;
}

.main {
    grid-area: main;
}

最終你會得到這樣的結構,

grid-template-areas 範例

當然,這種寫法不是沒有限制的,

  1. 命名規則組合必須要是矩形(N x M
  2. 不同區塊不能使用重複名稱
  3. 可以用 . 來忽略(不使用)該區域
  4. 任何非預期字串都會被視為空白

所以說,以下的寫法 皆不合法

/* 以下寫法不合法 */
.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar sidebar main"
        "sidebar main main";
}

/* 以下寫法不合法 */
.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar . main"
        "sidebar main main";

/* 以下寫法不合法 */
.grid-container {
    display: grid;
    grid-template-areas:
        "nav nav nav"
        "sidebar main main"
        "sidebar main"
        "sidebar main main";
}

另外,除了容器本身限制之外,當你的網格元件使用了 未命名 的網格單元時,會對隱性網格格線產生副作用。這個部分我後面會再次提及。


隱性網格

隱性網格(Implicit Grid)在網格系統中是一個很特別的存在。官方對他的說明簡單的解釋可以這麼形容,

你的設定不足以畫出一個網格,所以我雞婆的幫你把缺的格線補上。

何謂設定不足以畫出網格?例如,

  1. 沒有設定欄(grid-template-columns)或列(grid-template-rows
  2. 超出設定欄或列的數量
  3. 設定了 grid-area 卻不存在於 grid-template-areas 當中

隱性網格有兩個樣式可以使用,

  1. grid-auto-columns
  2. grid-auto-rows

設定的數值僅接受這些,

  • auto
  • min-content
  • max-content
  • fr
  • minmax()
  • 百分比單位數值(例如 10%

關於隱性網格在這邊就不贅述,後面會繼續提到這個東西。


網格單元順序

網格單元的順序其實跟 Flexbox 使用 order 的方式是一樣的,不過,他可以使用 z-index 來定義哪一個格子比較高。

Grid cell 使用 z-index

通常會搭配 z-index 大多數是網格容器本身有捲動需求的時候會特別這樣做。


對齊與間隔

基本上網格容器的對齊跟 Flexbox 幾乎是完全一樣。我在這邊如果再講一次好像又太浪費篇幅。基本上以下這些東西跟 Flexbox 完全一樣,

  • align-items
  • align-self
  • align-content
  • justify-content

然後網格系統多了這幾個,僅能在 Grid 容器下使用,

  • justify-items
  • justify-self
  • place-items
  • place-self
  • place-content

間隔的部分也跟 Flexbox 使用同一套 gap,其實就是共用同一套 w3c 的規範,

CSS Box Alignment Module Level 3

其實這邊沒有什麼新的東西,除了 Grid 才能使用的對齊方式外,其他的東西都跟 Flexbox 幾乎一樣。

justify-itemsjustify-self 是在容器系統中,Grid 單元主要軸向維度作對齊的樣式設定。與 align-items, align-self 則是交叉向維度做對齊(或填充)的樣式。

Grid Layout 並沒有指定主要軸與交叉軸,這邊的軸向設定是跟著系統的文字流向而決定的。所以他沒有像是 Flexbox 可以明確的指定誰是主要軸,誰是交叉軸。

以下稍微說明一下 Grid 多出來的幾樣東西,

樣式 設定值 預設值
justify-items normal, stretch, <baseline-position>, <overflow-position>?, [ <self-position>, left, right ], legacy, `legacy && [ left right
justify-self auto, normal, stretch, <baseline-position>, <overflow-position>? [ <self-position>, left, right ] auto
place-items <'align-items'> <'justify-items'>? 無特定預設值
place-self <'align-self'> <'justify-self'>? 無特定預設值
place-content <'align-content'> <'justify-content'>? 無特定預設值

基本上可以當作是在 Flexbox 上因為需要多行而無法特別設定的東西,在 Grid 裡面可以直接設定這樣的樣式。當然,如果你的 Grid 單元數量沒有到兩個欄或列以上,其實是看不出什麼效果的。


小記

今天先這樣,Grid 容器基本上東西太多,要一次講完有點困難。明天會搭配圖片來解說各種關於容器的設定,還有一些比較基本的運算方式。


目錄與小節:
[CSS] Flex/Grid Layout Modules, part 0


鐵人賽同步放送:
https://ithelp.ithome.com.tw/articles/10261748