グリッドレイアウトとは
レイアウトの仕組み
親要素グリッドコンテナーの中に、その直接の子要素グリッドアイテムをグリッド線で囲まれたグリッドセルに配置します。
一つの子要素に、複数のグリッドセルを割り当てることも可能です(グリッドエリア)。
フレックスボックスとの違い
フレックスボックスは一次元 – 一列又は一行 – のレイアウトのために設計されたという点です。グリッドは二次元 – 行と列が同時 – のレイアウトのために設計されました。
MDN グリッドレイアウトと他のレイアウト方法との関係 より引用
とのことですが、抽象的でわかる人には分かるような表現に感じます。
別のページの下記の方が分かりやすいです。
フレックスボックスが一次元であることは、フレックスボックスが一つの次元、つまり行か列のいずれかの方向にしかレイアウトしないことを述べています。これは CSS グリッドレイアウトが行と列の二次元を同時に制御するモデルであることと対照的です。
MDN フレックスボックスの基本概念 より引用
フレックスボックスレイアウトとの違いについて、説明を付け加えます。
子要素のサイズ
グリッドレイアウトは、子要素のスペース領域(グリッドエリア)、つまりサイズをグリッド線番号を使用して指定可能です。
子要素の間隔
グリッドレイアウトでは、子要素間の間隔を固定可能(もちろん間隔なしでもOK)です。
逆にいうと、子要素間の間隔を自動で調整しません。
フレックスボックスでも、間隔は調整可能ですが、その幅は子要素のサイズやその数に依存します。
折り返した時に、justify-contentの値にspace-evenlyやspace-around(flex-startでは整列して見えますが)を設定した場合、フレックスボックスの得意とする柔軟性 により、上下の行が整列しないことがあります。
例えば、子要素が5つあり、4つ目で折り返されて2行にわたる場合を想像してみてください。
各行それぞれで、子要素間の間隔が均等に計算されるため、子要素3つの1行目と子要素2つの2行目の子要素間の間隔が異なっていしまいます。
これは、各行でコンテナーが作成されるためです(おそらくこの特性を指して、一次元と表現しているのではないかと思われます)。
間隔を揃えて整列させることが、グリッドレイアウトの大きな特徴の一つとも言えます。
どちら選択すべきか
結論として、1列または1行のレイアウトを揃えたい場合は、フレックスボックスでも十分だと思います。
列や行が複数になるが整列させたい場合や、各子要素の領域を変更したい場合は、グリッドレイアウトの方が適しているかもしれません。
グリッドレイアウト作成方法
グリッドコンテナーを作成
親要素に対して下記のCSSを追加すると、この領域がグリッドレイアウトエリアとなります。
.container { /* 親要素 */
display: grid;
}
但し、この状態ではまだトラックはできていないので、行は分割されておらず、下記のように各グリッドアイテムには単に1行分割り当てられているだけです。
トラックを作成
列トラック及び行トラックを作成することにより、グリッドレイアウトのベースができます。
トラックの単位
固定サイズpx単位や、可変サイズの%単位に加え、frという単位でも指定可能です。
frとはグリッドコンテナー内の領域を等分に分割した単位です。
つまり、1frを3つ作成すれば、3分割されるということです。
実際に、次に説明する指定方法を見れば、容易に理解できると思います。
指定方法
下記の例では、列トラックは最初は100px、その次からは残りの領域を4分割し、2/4を1つ、1/4を2つ作成し、列トラック間隔は15pxとします。
そして、行トラック最初は20px、その次からは30pxを2つ作成し、行トラック間隔は1emとしています。
.container {
display: grid;
grid-template-columns: 100px 2fr 1fr 1fr;
grid-template-rows: 20px 30px 30px;
column-gap: 15px;
row-gap: 1em;
}
トラックや間隔が同一の場合は、省略した記述も可能です。
下記は、repeat() 記法を利用、自動でトラックを作成、gapを行列まとめて指定しています。
列トラックは1/4を4つ作成し、行トラックは100pxで数は自動で追加、列トラック間隔・行トラック間隔を同じ10pxとしています。
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: 100px;
gap: 10px
}
グリッド線を利用してグリッドアイテムの領域を変更
グリッドができたところで、各グリッドアイテムをどこの領域に配置するか指定することにより、子要素間の間隔を維持したまま、より複雑なレイアウトが可能になります。
CODEPEN
「Run Pen」をクリックしてください。
See the Pen grid_track_2 by blue moon (@blue-moon) on CodePen.
HTMLファイル
<body>
<p>グリッドコンテナー</p>
<div class="container">
<div class="item">item1</div>
<div class="item">item2</div>
<div class="item">item3</div>
<div class="item">item4</div>
<div class="item">item5</div>
<div class="item">item6</div>
<div class="item">item7</div>
<div class="item">item8</div>
<div class="item">item9</div>
<div class="item">item10</div>
</div>
</body>
CSSファイル
* {
box-sizing: border-box;
}
body {
font-size: 12px;
}
p {
margin: 10px 5% 0;
color: #d51f62;
}
.container {
margin: 10px auto;
width: 95%;
background: #fdebf2;
border: solid 1px #d51f62;
display: grid; /* グリッドレイアウト作成 */
grid-template-columns: 1fr 1fr 1fr; /* 列トラック作成 */
/* grid-template-columns: repeat(3, 1fr); 上記の別の書き方 */
grid-template-rows: 30px 30px 30px 30px; /* 行トラック作成 */
/* grid-template-rows: repeat(4, 30px); 上記の別の書き方 */
/* grid-auto-rows: 30px; 上記2つの別の書き方で自動で行を追加 */
column-gap: 5px;
row-gap: 5px;
/* gap: 5px; 上記2つの、column-gap: 5px;と、row-gap: 5px;をまとめて記述も可 */
}
.item {
padding-left: 5px;
border: solid 1px #0000df;
border-radius: 2px;
background: #6dc4ee;
line-height: 30px;
}
/* item1は、列トラックはグリッド線1~4まで */
.item:nth-child(1) {
grid-column-start: 1;
grid-column-end: 4;
}
/* item2は、列トラックはグリッド線1~2まで、行トラックはグリッド線2~4まで */
.item:nth-child(2) {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 2;
grid-row-end: 4;
}
.item:nth-child(8) {
grid-column-start: 2;
grid-column-end: 4;
}
.item:nth-child(9) {
grid-column-start: 1;
grid-column-end: 3;
}
下記では、グリッドレイアウトを使ってタイルを並べてみました。
グリッドレイアウトの応用
ネスト
子要素のグリッドアイテムを更にグリッドコンテナーにすることも可能です。
親要素のグリッドコンテナーの設定(トラックやgapの値)を継承することもありません。
次の例では、item7のグリッドアイテムを更にグリッドコンテナーにしています。
CODEPEN
See the Pen grid_nest by blue moon (@blue-moon) on CodePen.
HTMLファイル
<!-- これより上は省略 -->
<div class="item"><!-- item7のテキストは削除 -->
<div class="nest">item7-1</div>
<div class="nest">item7-2</div>
<div class="nest">item7-3</div>
<div class="nest">item7-4</div>
<div class="nest">item7-5</div>
<div class="nest">item7-6</div>
</div>
<!-- これより下は省略 -->
補足説明
入れ子のグリッドコンテナーを作成するitem7のテキストは削除しています。
これを残しておくと、この「item7」というテキスト自体にもグリッドセルが割り当てられ、グリッドコンテナー内に本来の子要素グリッドアイテム(クラス名「nest」)が収まらなくなるためです。
CSSファイル
/* これより上は省略 */
.item:nth-child(7) {
padding-left: 0; /* 親要素の「item7」という文字をpaddingしていたものを0に */
grid-column-start: 2;
grid-column-end: 4;
grid-row-start: 4;
grid-row-end: 6;
display: grid; /* グリッドレイアウトを入れ子で作成 */
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: 30px;
gap: 3px;
}
.item:nth-child(10) {
grid-column-start: 1;
grid-column-end: 4;
}
.nest {
padding-left: 5px;
border: solid 1px #f7c100;
border-radius: 2px;
background: #fff066;
font-size: x-small;
line-height: 30px;
color: #888;
}
グリッドアイテムをL字型
グリッドアイテムは基本四角形ですが、後のグリッドアイテムを先のグリッドアイテムに上書きする、または先のグリッドアイテムに後のグリッドアイテムより数値の高いz-indexを指定することにより、L字型にすることも可能です。
See the Pen grid_overwrite by blue moon (@blue-moon) on CodePen.
CSSファイル
/* これより上は省略 */
.container {
/* 途中省略 */
gap: 0; /* 間隔なし */
}
.item {
/* 省略 */
}
.item:nth-child(1) {
grid-column-start: 1;
grid-column-end: 4;
}
.item:nth-child(2) {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 2;
grid-row-end: 4;
}
.item:nth-child(3) {
grid-column-start: 2;
grid-column-end: 4;
grid-row-start: 2;
grid-row-end: 3;
}
.item:nth-child(5) {
grid-column-start: 1;
grid-column-end: 2;
grid-row-start: 4;
grid-row-end: 5;
z-index: 1;
}
.item:nth-child(6) {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 4;
grid-row-end: 6;
padding-left: calc(50% + 5px);
}
/* 残りの領域全部を、最後のグリッドアイテムに割り当てるという機能はない */
.item:nth-child(10) {
grid-column-start: 2;
grid-column-end: 4;
}
補足説明
item2は、item3に上書きされることでL字型になっています。
item2の終了位置のグリッド線が3、item3の開始位置のグリッド線が2となっており、このグリッド線2~3の間のトラックが重複しており、後のグリッドアイテムが上に被さります。
これは、CSSの順番ではなくHTMLのグリッドアイテムの順番です。
item6は、item5を上書きしていますが(両方の開始位置のグリッド線が1で、このグリッド線1~2の間のトラックが重複)、item5にitem6より大きいz-indexを設定することで、L字型になっています。
補足すると、行トラックは記述しなくても、グリッドアイテムの数により自動で追加されますが、サイズは自動調整されることになります。
よって、サイズは明示しておき、トラック数は自動とする方がよいと思っています。