相較於 Flexbox 著重在單一維度的切版 (row or column),Grid 能讓我們同時照顧兩個維度 (row and column)。CSS Grid 主要由 Grid Container 與 Grid Items 所組成,前者是父層容器,後者則是子層項目,概念和 Flexbox 非常相似。
CSS Grid 帶給切版更多彈性,舉個例子,像是英國衛報 (The Guardians) 的網站,就透過 CSS Grid 切出了豐富、複雜的版型。
Grid system 由一組水平線和一組垂直線交錯組合而成,這些相交而成的線被稱為「欄 (column)」和「列 (row)」。
要使用 CSS Grid,我們必須先在容器元素呼叫 display: grid
。接著便可以透過 grid-template-columns
以及 grid-template-rows
來控制整個版面。
這邊附上原先的 demo 環境程式碼:
<div class="container">
<div class="box" style="background-color: #E53935"> ONE</on></div>
<div class="box" style="background-color: #00897B">TWO</div>
<div class="box" style="background-color: #D81B60">THREE</div>
<div class="box" style="background-color: #8E24AA">FOUR</div>
<div class="box" style="background-color: #5E35B1">FIVE</div>
<div class="box" style="background-color: #3949AB">SIX</div>
<div class="box" style="background-color: #1E88E5">SEVEN</div>
<div class="box" style="background-color: #00ACC1">EIGHT</div>
</div>
.container {
height: 700px;
width: 700px;
margin: 200px auto;
background-color: black;
box-shadow: 0 6px 20px rgb(0 0 0 / 0.2);
}
.box {
color: white;
font-size: 1.5rem;
text-align: center;
}
在容器元素加上 display: grid
之後,子元素項目的寬度和高度都會占滿整個容器。
.container {
display: grid; /* 加入這一行 */
height: 700px;
width: 700px;
margin: 200px auto;
background-color: black;
box-shadow: 0 6px 20px rgb(0 0 0 / 0.2);
}
除了 display: grid
之外,還有另一個選擇是 display: inline-grid
,效果和 grid 大同小異,差異點在於它會將 container 變成 inline 元素。如果我們想要讓 container 和其他 inline 屬性的元素處在同一行 (例如 <a>
或 <span>
等等),就滿適合使用 display: inline-grid
。
前面提過,CSS Grid 有別於 Flexbox 的地方在於二維度排版,而這就有賴於 grid-template-columns
以及 grid-template-rows
了。在 Grid 的世界中,我們仍舊可以使用 50% 這樣的相對單位,但更常見的單位是 fr
。
fr
代表 fraction,也就是我們要把版面切成多少塊的概念,把真實的寬高計算交給瀏覽器,輕鬆愜意。底下示範把畫面切成 3 欄,寫上 3 個 1fr 即可。中途就算改變 container 的 width
屬性,Grid 照樣會依循 3 等分的原則縮放。
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* 加入這一行 */
height: 700px;
width: 700px;
margin: 200px auto;
background-color: black;
box-shadow: 0 6px 20px rgb(0 0 0 / 0.2);
}
fr
前面的數字不只代表欄數或行數,也象徵著欄、行之間的比例關係。假設我們改成 grid-template-columns: 1fr 2fr 1fr
,畫面一樣會被切割為 3 欄,但中間那一欄比其他欄還要寬一倍。
除此之外,fr
可以搭配絕對單位與相對單位一起使用,比方說 grid-template-rows: 150px 1fr 150px
,我們將畫面切割成 3 行,第一和最後一行為 150px,而第二行則會占滿剩下的部分。
至於將 grid-template-rows
以及 grid-template-columns
一起使用,則可以讓版面更加豐富。
.container {
display: grid;
grid-template-columns: 1fr 1fr; /* 加入這一行 */
grid-template-rows: 1fr 2fr 1fr 2fr; /* 加入這一行 */
height: 700px;
width: 700px;
margin: 200px auto;
background-color: black;
box-shadow: 0 6px 20px rgb(0 0 0 / 0.2);
}
不過每次都要分開寫 grid-template-rows
還有 grid-template-columns
也是挺麻煩的,這時候可以善用 grid shorthand 簡寫:
grid-template: <row-values> / <column-values>
以上面的範例來說,就可以改寫成這樣:
grid-template: 1fr 2fr 1fr 2fr / 1fr 1fr;
除了將欄與行控制在固定大小之外,我們也能透過 minmax()
來 Grid 大小的範圍值。裡面需要帶入兩個參數,前面放最小值,半形逗號隔開後面加上最大值。
grid-template: minmax(<min value>, <max value>)
假設我們使用 grid-template-columns: minmax(400px, 600px)
,當畫面寬度超過 600px,Grid items 將佔不滿整個 container。
相對來說,當畫面小於 400px 就會被切掉,出現滾輪。
如果某一欄的內容很長的話,將最大值設為 auto 也是不錯的選擇,這樣就不會出現內容溢出的窘況了。
minmax(<min value, auto>)
repeat()
每次都要慢慢手打單位是件很麻煩的事情,假設有 8 個 1fr,光想像頭就開始痛了。這時候善用 repeat()
,對手腕和心靈健康都很有幫助。
grid-template: repeat(<repeat times>, <unit values>)
要切出同等份的 8 欄,可以這樣寫:
grid-template-columns: repeat(8, 1fr);
搭配剛剛提到的 mimmax()
也完全沒問題:
grid-template-columns: repeat(4, minmax(300px, auto));
fit-content()
若希望 grid items 可以隨著內容縮放,不妨用用看看 fit-content()
。裡面只需要帶入一個參數:最大值。若帶入 300 px,就代表 grid items 會隨著內容擴張,直到 300px 為止。
.container {
display: grid;
grid-template-columns: repeat(3, fit-content(300px));
height: 700px;
width: 700px;
margin: 200px auto;
background-color: black;
box-shadow: 0 6px 20px rgb(0 0 0 / 0.2);
}
在上方的例子中,我們將畫面切割成 3 欄,並規定每一欄都隨其內容擴張,直到觸及最大值 300px 為止。
gap()
Flexbox 提供了 gap()
讓我們輕鬆控制 flex items 之間的空隙,Grid 也有提供。我們可以用 row-gap
或是 column-gap
來指定要控制行或是列的空隙大小。
.container {
display: grid;
grid-template: 1fr 1fr / repeat(4, 1fr);
column-gap: 20px; /* watch me baby! */
row-gap: 30px; /* watch me baby! */
height: 700px;
width: 700px;
margin: 200px auto;
background-color: black;
box-shadow: 0 6px 20px rgb(0 0 0 / 0.2);
}
覺得分成兩行寫太麻煩的話,也可以參考 grid-gap
的 shorthand 寫法,如果只帶入一組設定值,那 row gap 和 column gap 都會套用。帶入兩組設定值的話,前面是 row gap,後面是 column gap。
.container {
...
grid-gap: 20px /* Both row-gap and column-gap are 20px */
grid-gap: 20px 30px /* Row gap equals to 20px while column gap is 30px */
}