[CSS] Grid Layout
TL;DR
- grid 和 flexbox 有一個關鍵的差異是,grid 需要用 2D 的角度來思考畫面的排版。
Snippets
- Less Absolute Positioning With Modern CSS:透過 CSS Grid 不用使用 position absolute,並做出常見的樣式。
/**
* Responsive Tiles
*/
.grid-container {
// 使用 auto-fill 讓它自動決定一行要有幾個 columns
// 使用 minmax,使得 grid-item 最小至少要 300px,最大則會自己延伸
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
grid-auto-rows: 1fr; // 如果需要讓所有的 gird-item 等高
}
See the Pen Grid Image Tile Layout by PJCHEN (@PJCHENder) on CodePen.
常用屬性
.container {
display: grid;
/* 定義 grid 之間的間距 */
grid-row-gap: 10px;
grid-column-gap: 20px;
grid-gap: <grid-row-gap> <grid-column-gap>;
/* 定義 explicit track */
grid-template-columns: 100px auto 5rem;
grid-template-rows: 200px 10px;
// grid-template 是 grid-template-rows / grid-template-columns 的縮寫
grid-template: repeat(3, 1fr) / repeat(2, 1fr);
/* 定義 implicit track */
grid-auto-rows: 500px;
grid-auto-columns: 300px;
/* 定義多餘的元素要以什麼方式排列 */
grid-auto-flow: column | row(default) | dense;
/* 定義 grid-area 的名稱 */
grid-template-areas: 'foo bar';
/* 對齊與置中 */
/*
start | end | center |
baseline | first baseline | last baseline |
flex-start | flex-end | left | right |
space-between | space-around | space-evenly |
stretch(default);
*/
align-items: baseline | stretch(default); // no left, right
justify-content: left | right | stretch(default); // no baseline
align-content: start | end | center | stretch(default);
place-content: <align-content> <justify-content>;
/* repeat (times, unit) */
grid-template-columns: repeat(4, 1fr); /* 1fr 1fr 1fr 1fr */
/* minmax 搭配 auto-fit */
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.grid-item {
/* 定義該 item 要佔據幾個 column 或 row */
grid-column-start: 1;
grid-column-end: 4;
grid-row-start: 1;
grid-row-end: 3;
// grid-column 是 grid-column-start / grid-column-end 的縮寫
grid-column: 2 / span 2; // 從 column 2 開始算起,佔據 2 個 column
grid-column: span 3; // 從現在的位置算起,佔據 3 個 column
grid-column: 1 / 3; // 從 column 1 開始到 column 3
grid-column: 1 / -1; // width: 100%
grid-row: span 2; // 從現在的位置算起,佔據 2 個 row
// row-start / column-start / row-end / column-end
grid-area: 1; // 等同於 1/auto/auto/auto
grid-area: 1 / 1 / 2 / 3;
/* 套用 grid-area 的名稱*/
grid-area: foo;
/* 對齊與置中 */
align-self: start | end | center | stretch(default);
justify-self: start | end | center | stretch(default);
}
目錄
[TOC]
參考
- Get Started with Grid Layout @ Grid by Example
- A Complete Guide to Grid @ CSS Tricks
- CSS Grid @ Wes Bos
- CSS Grid Layout Guides @ MDN - Web technology for developers
重要觀念
- 當我們在定義 grid 的時候,是透過定義 grid track (grid cell) 來達到,而不是透過定義 grid-line。但是當我們要放置 grid-item 的時候,則是透過 grid line,而非 grid track。
- grid-auto-flow:預設的情況下,當我們只定義兩欄,但卻有超過兩個 grid-item 時,多出來的 grid-item 會自動換到下一列,這時候我們可以使用
grid-auto-flow: column
調整成不換行。 - 使用單位 fr (fraction):在
grid-template-columns
或grid-template-rows
中,若我們使用的是 % 作為單位時,它會以外層 container 的寬度當作總寬去計算百分比,但它不會把grid-gap
的寬度考慮進去,因此使用 % 計算時,常常會超出整個 container 的寬度。這時候我們可以使用新的單位-fr,它會把grid-gap
的寬度也算進去之後,剩餘的容器寬度下去做分配,分配的方式不是百分比,而是像 flex box 中的flex-grow
和flex-shrink
類似,按照每一個 item 所給的fr
加以計算。 - 容器高度:在預設
grid-auto-flow: row
的情況下,多出來的 grid-item 會自動換 到下一行。這時候容器的高度或根據 item 的高度來自動延展,因此若使用grid-template-rows
並搭配 fr 為單位時,有時候需要在 container 設定高度,才能有自動擴展的效果。
專有名詞(terms)
名詞 | 說明 | 圖示 |
---|---|---|
explicit track | 在 MDN 中稱為 explicit grid line,可以透過 grid-template-column 和 grid-template-rows 來定義。 | 虛線(dashed line) |
implicit track | 在 MDN 中稱為 implicit grid line,可以透過 grid-auto-rows 和 grid-auto-columns 來定義。 | 點線(dotted line) |
gap | 在 MDN 中稱為 gutter 或 alleys,可以透過 column-gap , row-gap , 或簡寫的 gap 來定義。 | 斜虛線(diagonal dashed line) |
explicit grid edge | 實線(solid line) | |
grid track | 兩條線之間的區域。 | |
grid cell | grid 的最小單位。 | |
grid area | 多個 grid cell 可以組成 grid area,它一定是方形的,不會是 L 型的。 |
explicit 和 implicit grid track
Explicit 和 implicit 的差別 在於 explicit 是使用者透過 CSS 定義產生的,而 implicit 則是瀏覽器將剩餘的空間自行運用產生的:
- explicit 的 grid 可以透過
grid-template-columns
和grid-template-rows
來定義,剩餘多出來的 grid 都視為 implicit grid。 - implicit grid 預設的寬度會根據內容的大小來改變,若想改變 implicit grid 的話,可以透過
grid-auto-rows
和grid-auto-columns
來定義。
grid-container
定義 grid track 的欄寬和列高(grid-template-columns, grid-template-rows)
px
,rem
:直接定義想要的值。fr
:fraction,指的是 container 扣掉 grid-gap 後的空間加以分配,用法類似flex-grow
和flex-shrink
。auto
:如果搭配的單位內有fr
這類彈性變化的單位使用,則會以 grid-item 本身原本內容的寬度呈現;如果搭配的只有px
或rem
這類固定的單位,則會自動填滿所有剩餘的空間(見 [fit-content() 和 auto 的使用](#fit-content() 和 auto 的使用))。
.grid-container {
grid-template: <grid-template-rows> / <grid-template-columns>; /* 縮寫 */
grid-template-columns: auto 50px auto;
grid-template-rows: 10px 10px 10px;
}
定義 grid item 間距的寬度(grid-gap)
.grid-container {
grid-gap: <grid-row-gap> <grid-column-gap>; /* 縮寫 */
grid-row-gap: 20px;
grid-column-gap: 20px;
}
定義 grid item 排列的方式(grid-auto-flow)
.grid-container {
grid-auto-flow: row(default) / column / dense;
}
當每一個 grid 的 寬度和高度都相同時
當 grid 的寬高有所不同時
.grid-container {
display: grid;
grid-gap: 20px;
grid-auto-flow: dense;
grid-template-columns: repeat(10, 1fr);
}
.grid-item:nth-child(3n) {
background-color: cornflowerblue;
grid-column: span 5;
}
.grid-item:nth-child(5n) {
background-color: tomato;
grid-column: span 4;
}
.grid-item:nth-child(7n) {
background-color: greenyellow;
grid-row: span 2;
}
- 使用預設的
grid-auto-flow: row;
則不考慮 grid-item 的大小,僅依照順序將 grid-item 填入,但若放不進去,則會換行,因此較容易留有空白。 - 使用
grid-auto-flow: dense;
則會先盡可能依照大小把可以放入的 grid-item 填入,接著才考慮順序,最後放不下時才換行,較不易出現空白(但不保證一定沒空白),特別適合用在不那麼重視順序(例如,相片牆)的情況。
對齊與置中(justify-content, justify-items, place-items)
justify-content
/align-content
:用來對齊 grid-container 內的 grid-item,適用在以絕對單位定義寬度(例如,px
),使得 grid-container 有多餘的空間時。justify-items
/align-items
:用來對齊 grid-item 裡面的內容(content)。如果只要針對某個 grid-item ,可以針對該 item 下align-self
或justify-self
。place-items
:是 align-items 和 justify-items 的縮寫,前者表示 align-items,後者為 justify-items,例如place-items: center
或place-items: center start;
。
- justify-xxx 適用於 x-axis;algin-xxx 適用於 y-axis。
- 使用 align-content 時,最好幫 grid-container 設 height,且不要設定 grid-template-rows,效果會比較明顯(但是通常 grid-container 是不會設定 height 的)。
.grid-container {
justify-content: start | end | center | stretch(default) | space-around | space-between | space-evenly;
justify-items: start | end | center | stretch(default);
}
縮寫
.grid-container {
grid: none | <grid-template-rows> / <grid-template-columns> | <grid-auto-flow> [<grid-auto-rows> [/ <grid-auto-columns>]];
}
.grid-container {
grid: 200px auto / 1fr auto 1fr;
}
/* 等同於 */
.grid-container {
grid-template-rows: 200px auto;
grid-template-columns: 1fr auto 1fr;
grid-template-areas: none;
}
grid-item
定義要佔據多少欄或 列(grid-column, grid-row)
grid-column
可以用來定義該 item 要佔據幾個 column,預設是grid-column: span 1
。- 當所設定的 grid-column 欄數過多而無法在該列容納下時,會自動換到下一行。
- 當所設定的 grid-column 欄數超過
grid-template-column
所設定的欄數時,會跑出新的 implicit grid column。
使用 span <num>
可以用來定義要佔據幾個:
grid-column: span 2; /* 該 grid 會佔據 2 個欄寬 */
也可以透過 grid-column-start
和 grid-column-end
定義該 grid 要從第幾個 track 開始到第幾個 track 結束:
.grid-item {
/* short hand: grid-column: 3 / 5; */
grid-column-start: 3; /* 從 track 3 開始 */
grid-column-end: 5; /* 到 track 5 結束 */
}
也可以同時使用 span <num>
和 grid-column short hand
:
.grid-item {
/* 表示這個 grid-item 要佔據兩個欄寬,並以 track 5 的位置作為結束 */
grid-column: span 2 / 5;
/* 表示這個 grid-item 要佔據三個欄寬,並以 track 2 的位置作為開始 */
grid-column: 2 / span 3;
/* 表示這個 grid-item 要從,並以 track 2 的位置作為開始,track 5 的位置作為結束 */
grid-column: 2 / 5;
/* 表示這個 grid-item 將佔據整個 row ,類似 width: 100% */
grid-column: 1 / -1;
}
排序(order)
使用 order
可以為 grid-item 重新排序,但要留意的是,沒使用 order 的 grid-item 會排在最面,接著才是有使用 order 的。另外,order 只是顯示時的順序改變,在瀏覽器的 DOM 並沒有真的改變它的位置,因此在
.grid-item {
order: 1;
}
對齊與置中(justify-self, align-self)
- justify-self / align-self:用來針對單個 gird-item 中的 content 進行對齊。如果要改變所有 grid-items ,可以在 grid-container 中使用
align-items
和justify-items
。
.grid-item {
align-self: start | end | center | stretch(default);
justify-self: start | end | center | stretch(default);
}
grid function
repeat(times, unit)
grid-template-columns: repeat(4, 1fr); /* 1fr 1fr 1fr 1fr */
grid-template-columns: repeat(2, 1fr 2fr); /* 1fr 2fr 1fr 2fr */
grid-template-columns: 100px repeat(2, 1fr) 200px; /* 100px 1fr 1fr 200px */
auto-fill & auto-fit
auto-fill
和 auto-fit
都會自動根據 grid-container 的寬度調整欄數並換行,但是當 item 的數目不足以換行時,兩者的表現會有些微差異:
auto-fill
:當 grid-container 還有空間時,會繼續往後延伸新的 grid。auto-fit
:當 grid-container 還有空間時,不會繼續往後延伸新的 grid。
repeat(auto-fill, 100px)
repeat(auto-fit, 100px)
minmax()
當我們做 RWD 時,常會希望 grid-item 中的項目可以隨著 container 自動延展,但卻又希望它的大小至少不要小於某一個值的時候,經常會使用到 minmax(min, max)
這個 function,第一個參數表示最小值,第二個參數表示最大值:
/**
* 搭配 auto
* minmax(100px, auto) 中的 auto 表示它會「根據內容的尺寸」來調整大小。
**/
.grid-container {
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(100px, auto); /* 最小 100px,最大根據內容尺寸 */
}
/**
* 搭配 fr
**/
.grid-container {
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(150px, fr); /* 最小 150px,否則大家分配相同的空間 */
}
minmax()
經常搭配 auto-fit
或 auto-fill
使用,做出不同的效果:
/**
* 搭配 auto-fit 時,因為當 grid-container 還有空間時,不會繼續往後延伸新的 grid。
* 因此 grid-item 會根據 grid-container 的寬度佔滿整個 grid-container
**/
.grid-container {
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
/**
* 搭配 auto-fill 時,因為當 grid-container 還有空間時,會繼續往後延伸新的 grid。
* 因此 grid-item 不會自動填滿整個 grid-container 的寬度,
**/
.grid-container {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
fit-content() 和 auto 的使用
auto
-
當
grid-template-columns
裡面的單位全都是絕對單位(例如,px
),則使用auto
時該 gird-item 會自動延展 grid-container 中剩餘的空間。 -
當
grid-template-columns
裡面的單位有彈性單位(例如,fr
),則使用auto
時該 gird-item 會根據 grid-item 的內容自動展開(不會讓 grid-item 換行)。
fit-content()
透過 fit-content
可以讓 grid-item 的內容一定不會超出 grid-item 的寬度(需要時會自動換行)
- 當
fit-content()
內給的值比 grid-item 中的內容寬小時,會自動換行,並縮小到可縮的最小寬度。 - 當
fit-content()
內給的值比 grid-item 中的內容寬大時,效果會和有給 auto 和彈性長度時的效果一樣。
其他
為 grid track 命名:grid-template-areas & grid-area
可以在 grid-container 中透過 grid-template-areas
定義每一個 grid-area (i.e., cell) 的名稱;接著可以在 grid-item 中選擇套用的 grid-area
,該 grid-item 會自動填滿所定義的 grid-area。
使用上可以類似 flex-box 中的 order,當在不同的裝置尺寸下時(搭配 @media
),套用不同的 grid-template-area
,即可重新定義整 個 layout。
最好能先清楚透過 grid-template-columns 和 grid-template-rows 定義每個 grid-area。
/**
* grid-template-areas 套用在 grid-container 上
* 透過雙引號包住一個 row
* 如果有不想命名的 grid-area 可以使用 "." 代替
**/
.grid-container {
grid-template-columns: 1fr 500px 1fr;
grid-template-rows: 150px 150px 100px;
grid-template-areas:
'sidebar1 content sidebar2'
'sidebar1 content sidebar2'
'footer footer footer';
}
/**
* grid-area 套用在 grid-item 上
* 指定對應到的名稱後,該 grid-item 會填滿整個 grid-area
**/
.grid-item.sidebar1 {
grid-area: sidebar1;
}
.grid-item.sidebar2 {
grid-area: sidebar2;
}
.grid-item.content {
grid-area: content;
}
.grid-item.footer {
grid-area: footer;
}
為 grid-area 命名後,也可以在 grid-item 中透過 xxx-start
和 xxx-end
選擇它作為開始和結束的指標:
/* 可以直接在 grid-item 中使用定義好的名稱,不需加上雙引號 */
.grid-item {
grid-column: foo-start / bar-end;
}
為 grid-line 命名:grid-template-columns, grid-template-rows
如果需要為 grid-line 命名,也可以在 grid-container 的 grid-template-columns
或 grid-template-rows
中使用 []
來定義 grid-line 的名稱,接著就可以直接在 grid-item 中的 grid-column
或 grid-row
使用。
/* 在 grid-container 中使用 [] 定義 track 名稱 */
.grid-container {
display: grid;
grid-gap: 20px;
grid-template-columns: [site-left] 1fr [content-start] 500px [content-end] 1fr [site-right];
grid-template-rows: [content-top] repeat(3, auto) [content-bottom];
}
/* 可以直接在 grid-item 中使用定義好的名稱,不需加上雙引號 */
.grid-item.item3 {
background-color: steelblue;
grid-column: content-start / content-end;
grid-row: content-top / content-bottom;
}
實際應用
建立 Grid Layout
grid-system-layout @ frontend masters
.grid-container {
display: grid;
grid-gap: 20px;
grid-template-columns: repeat(10, 1fr); /* 建立每列有 10 個 column 的 grid */
}
.grid-item {
grid-column: span 2; /* 定義該 item 要佔據幾個 column */
}
.w-100 {
grid-column: 1 / -1; /* 該 item 會佔據整列 */
}