はじめに
CSS グリッドレイアウトはHTML/CSSを使って2次元レイアウトを柔軟に実現できるCSSの仕様です。
2022年6月にIE11のサポートが終了したことはまだ記憶に新しいですが、IEのサポート終了によってグリッドレイアウトを本格的に活用できるようになった方も多いのではないでしょうか。
グリッドレイアウトはこれまでの実装方法によりも簡単に要素の配置を操作できたり、複雑な表現を可能にしたりする非常に強力な仕様です。
一方で、機能や関連プロパティが多いためなんとなくいい感じにレイアウトできることもあれば、逆になぜかわからないけど思ったようにならなかったり…と詳細を理解して自由自在に扱うことは容易くありません。
そこでこの記事では、グリッドレイアウトをマスターして使いこなすために知っておきたい配置アルゴリズムを解説します。
前提知識
グリッドレイアウトを使う
CSSのレイアウトモードをグリッドレイアウトにするには、要素の display
プロパティに grid
もしくは inline-grid
を指定します。
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
.container {
display: grid;
}
.container
の要素はグリッドコンテナとなり、その子要素である.item
はグリッドアイテムとして扱われ、グリッド上に並べることができます。
グリッドライン
グリッドラインとは、グリッドの水平方向と垂直方向の区切り線のことです。
グリッドトラック
グリッドトラックは、任意の2本のグリッドライン線で挟まれた空間(行/列)を指します。
グリッドトラックを生成するとグリッドアイテムがグリッドトラック上に配置されます。
グリッドトラックには明示的なグリッドトラック(explicit grid tracks) と暗黙的なグリッドトラック(implicit grid tracks) があります。
明示的なグリッドトラック
下記の3つのプロパティで作成されたグリッドトラックは明示的なグリッドトラックと呼ばれます。
grid-template-rows
grid-template-columns
grid-template-areas
明示的なグリッドトラックのサイズはgrid-template-rows
/grid-template-columns
の値によって決定します。
grid-template-areas
で生成し、grid-template-rows
/grid-template-columns
を指定しない場合はgrid-auto-rows
/grid-auto-columns
の値によって決まります。
grid-template-rows
を使用してグリッドトラックを生成
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
.container {
display: grid;
grid-template-rows: repeat(3, 80px);
}
See the Pen Untitled by otky (@otky) on CodePen.
grid-template-rows - CSS: カスケーディングスタイルシート | MDN
grid-template-columns
を使用してグリッドトラックを生成
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}
See the Pen Untitled by otky (@otky) on CodePen.
grid-template-columns - CSS: カスケーディングスタイルシート | MDN
grid-template-areas
を使用してグリッドトラックを生成
<div class="container">
<div class="item-a">A</div>
<div class="item-b">B</div>
<div class="item-c">C</div>
</div>
.container {
display: grid;
grid-template-areas: "a a"
"b c";
}
.item-a {
grid-area: a;
}
.item-b {
grid-area: b;
}
.item-b {
grid-area: c;
}
See the Pen Untitled by otky (@otky) on CodePen.
grid-template-areas - CSS: カスケーディングスタイルシート | MDN
暗黙的なグリッドトラック
明示的なグリッドトラックがない場合や、作成したグリッドトラックの数よりグリッドアイテムの数が多い場合にはトラックが自動的に拡張されます。
これは暗黙的なグリッドトラックと呼ばれます。
暗黙的なグリッドトラックのサイズはgrid-auto-rows
/grid-auto-columns
の値によって決まります。
<div class="container">
<div class="item">A</div>
<div class="item">B</div>
<div class="item">C</div>
</div>
.container {
/* 明示的なグリッドトラックなし */
display: grid;
}
See the Pen Untitled by otky (@otky) on CodePen.
一見わかりづらいですが、デベロッパーツールから明示的なグリッドトラックの指定がなくてもグリッドトラックが生成されていることが確認できます。
グリッドレイアウトの配置
いよいよ本題です。
グリッドアイテムは、プロパティを指定することにより明示的に配置することも自動で配置することも可能です。
明示的に位置を指定しない場合、グリッドアイテムはグリッド上の各セルに1つずつ配置されます。
自動配置の方向
grid-auto-flow
プロパティを使用することで自動配置の方向を指定できます。
-
grid-auto-flow: row;
左から右 → 上から下の順にグリッドを埋めていき、暗黙的なグリッドトラックは新しい行として下に生成されます -
grid-auto-flow: column;
上から下 → 左から右の順にグリッドを埋めていき、暗黙的なグリッドトラックは新しい列として右に生成されます
grid-auto-flow
の初期値はrow
なので、grid-auto-flow
を指定しない場合は左から右、上から下の順に配置されます。
無名グリッドアイテム
無名(Anonymous)グリッドアイテムは、タグで囲まず直接グリッドコンテナに配置されたテキストがある場合に生成されます。
スタイルを指定できないため常に自動配置されます。
自動配置されることを把握していない場合、思わぬ場所に表示されることがあるので注意が必要です。
<div class="container">
<div class="item">A</div>
B <!-- 無名グリッドアイテム -->
<div class="item">C</div>
</div>
.container {
display: grid;
}
配置のステップ
グリッドレイアウトでは次のステップで配置が行われます。
- 自動配置されないグリッドアイテムを配置
- 明示的に行の位置が指定されているグリッドアイテムを配置
- 暗黙的なグリッドトラック(列)の数を決定
- 残りのグリッドアイテムを配置
通常の場合、グリッドアイテムはDOMの順番で処理されます。
ただし、order
プロパティによってグリッドアイテムの並び順が指定されている場合はその順番にしたがって処理されます。
order - CSS: カスケーディングスタイルシート | MDN
以下ではgrid-auto-flow: row
であると仮定します。
column
を指定したパターンが知りたい場合は「列(column)」→「行(row)」のように読み替えてください。
1. 自動配置されないグリッドアイテムを配置
まず、明示的な位置の指定があり、自動配置されないグリッドアイテムを配置します。
下記の例では、「B」のグリッドアイテムのみ明示的な位置の指定があります。
このステップでは他のグリッドアイテムは無視されます。
<div class="container">
<div class="item">A</div>
<div class="item explicitly-layout">B</div>
<div class="item">C</div>
<div class="item">D</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(2, 1fr);
}
.explicitly-layout {
/* 以下と同義
grid-row-start: 2;
grid-column-start: 1;
grid-row-end: 3;
grid-column-end 3; */
grid-area: 2 / 1 / 3 / 3;
}
grid-area
は grid-row-start
/ grid-column-start
/ grid-row-end
/ grid-column-end
一括指定プロパティで、指定する値によってグリッドアイテムの位置とサイズを決定できます。
grid-area - CSS: カスケーディングスタイルシート | MDN
See the Pen Untitled by otky (@otky) on CodePen.
grid-area - CSS: カスケーディングスタイルシート | MDN
2. 明示的に行の位置が指定されているグリッドアイテムを配置
grid-row-start
と grid-row-end
プロパティの値によって明示的に行の位置が確定しているグリッドアイテムを配置します。
このアイテムは列の位置は明示的に指定されていないため、自動で処理されます。
この列の自動配置のアルゴリズムにはsparse
とdense
の2種類のパッキングアルゴリズムがあります。
これらはgrid-auto-flow
で指定することができ、初期値はsparse
です。
sparse
の場合
グリッドアイテムの列の開始位置は、このステップで同じ行に既に配置したほかのグリッドアイテムより後の開始位置で、ほかのグリッドアイテムと重なり合わない最小の開始位置に設定されます。
<div class="container">
<div class="item item-a">A</div>
<div class="item item-b">B</div>
<div class="item item-c">C</div>
<div class="item item-d">D</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.item-a {
grid-area: 1 / 2 / 2 / 3;
}
.item-b {
grid-area: 2 / 1 / 2 / 3;
}
.item-c {
grid-row: 1 / 3;
}
.item-d {
grid-row: 1 / 2;
}
See the Pen sparse by otky (@otky) on CodePen.
dense
の場合
グリッドアイテムの列の開始位置は、既に配置したほかのグリッドアイテムと重なり合わない最小の開始位置に設定されます。
つまり、これまでに配置されたグリッドアイテムに隙間があり、これから配置するグリッドアイテムがそこにおさまるのならDOM(やorder
)の順番を無視してそこに配置されます。
<div class="container">
<div class="item item-a">A</div>
<div class="item item-b">B</div>
<div class="item item-c">C</div>
<div class="item item-d">D</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
/* ↓ この行を追加 */
grid-auto-flow: dense;
}
.item-a {
grid-area: 1 / 2 / 2 / 3;
}
.item-b {
grid-area: 2 / 1 / 2 / 3;
}
.item-c {
grid-row: 1 / 3;
}
.item-d {
grid-row: 1 / 2;
}
See the Pen Untitled by otky (@otky) on CodePen.
3. 暗黙的なグリッドトラック(列)の数を決定
暗黙的なグリッドトラック(列)の数を決定します。
まず、列の位置が確定しているすべてのグリッドアイテム(明示的に配置されたグリッドアイテムや前のステップで配置されたグリッドアイテム、まだ配置されていないが列が確定しているグリッドアイテム)を配置するために必要な列を暗黙的なグリッドトラックとして追加します。
次に、列の位置が確定していないすべてのグリッドアイテムのうち、最大の列幅が暗黙のグリッドの幅より大きい場合はその列幅に対応するように暗黙のグリッドトラックを末尾に追加します。
4. 残りのグリッドアイテムを配置
最後に残りのグリッドアイテムを配置します。
ここで 自動配置カーソル(auto-placement cursor) という概念が登場します。
自動配置カーソルはグリッドにおける現在の挿入位置を表し、グリッドラインの行と列のインデックスを指定します。
初期状態は最初の暗黙的なグリッドトラックの初めの行と列が設定されています。
このステップでもgrid-auto-flow
の値のパッキングアルゴリズムにしたがって配置されます。
sparse
の場合
デフォルトではsparse
として処理されます。
グリッドアイテムが明示的な列の位置が定義されてるとき
1.自動カーソルの列をグリッドアイテムが開始されるグリッドラインに設定します。
この値が自動カーソルの前の列位置より小さい場合は、行を1つ増やします。
2.他のグリッドアイテムと重ならない位置が見つかるまで行を1つずつ増やしていきます。
この時、必要に応じて暗黙的なグリッドトラック(行)を作成します。
3.他のグリッドアイテムと重ならない位置が見つかったら、グリッドアイテムの行の開始位置は自動カーソルの行の位置に設定されます。
グリッドアイテムの位置が定義されていなとき
- 既に配置されているグリッドアイテムと重ならない値が見つかるまで自動配置カーソルの列の値を1つずつ増やします。
現在の行では重ならない値見つからず、カーソルの列位置とグリッドアイテムの列幅が前の手順で決定した暗黙的なグリッドトラックの列数をオーバーフローする場合は自動配置カーソルの行を1つを増やします。
この時、必要に応じて暗黙的なグリッドトラック(行)を作成します。
自動配置カーソルの列を暗黙のグリッドの最初の列に設定し、この手順を繰り返します。 - グリッドアイテムと重ならない値が見つかったら、このグリッドアイテムの行の開始位置と列の開始位置を自動配置カーソルの位置に設定します。
次のグリッドアイテムを配置する際の自動カーソルの位置は、直前のグリッドアイテムの位置から始めます。
下記の例で実際に見てみましょう。
<div class="container">
<div class="item item-a">A</div>
<div class="item item-b">B</div>
<div class="item item-c">C</div>
<div class="item item-d">D</div>
<div class="item item-e">E</div>
<div class="item item-f">F</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.item-a {
grid-area: 1 / 2 / 2 / 3;
}
.item-b {
grid-area: 2 / 1 / 2 / 3;
}
.item-c {
grid-row: 1 / 3;
}
.item-d {
grid-row: 1 / 2;
}
.item-e {
grid-column: 2 / 4;
}
See the Pen Remaining Sparse items by otky (@otky) on CodePen.
- E → 明示的な列の位置があるグリッドアイテム
- F → 位置が定義されていないグリッドアイテム
E には明示的な列位置の指定があります。
.item-e {
grid-column: 2 / 4;
}
1行目、2行目のグリッドトラックでこのgrid-column
を満たす位置には既にアイテムがあって配置することができないため、3行目に配置されます。
ここで自動配置カーソルの位置はEのrow-start
とcolumn-start
の値、つまり3行目/2列目に設定されます。
Fは明示的な位置が定義されていないため、この自動配置カーソルの位置から既存のアイテムに重ならない位置を探して配置されます。
dense
の場合
sparse
と違いは、グリッドアイテムを配置配置する際に毎回自動カーソルの初期位置が暗黙的なグリッドトラックの開始位置になることです。
先ほどのsparse
の例のコードにgrid-auto-flow: dense
を指定して見てみましょう。
<div class="container">
<div class="item item-a">A</div>
<div class="item item-b">B</div>
<div class="item item-c">C</div>
<div class="item item-d">D</div>
<div class="item item-e">E</div>
<div class="item item-f">F</div>
</div>
.container {
display: grid;
grid-template-columns: repeat(4, 1fr);
// ↓ この行を追加
grid-auto-flow: dense;
}
.item-a {
grid-area: 1 / 2 / 2 / 3;
}
.item-b {
grid-area: 2 / 1 / 2 / 3;
}
.item-c {
grid-row: 1 / 3;
}
.item-d {
grid-row: 1 / 2;
}
.item-e {
grid-column: 2 / 4;
}
See the Pen remaining dense items by otky (@otky) on CodePen.
grid-auto-flow: dense;
を指定したことで行のみ明示的な位置が定義されているDの位置が移動しています。( 2. 明示的に行の位置が指定されているグリッドアイテムを配置 を参照)
自動配置カーソルの初期位置は行/列共に1の位置です。
Eはsparse
の場合と同様に配置されますが、自動配置カーソルの位置は初期の行/列共に1の位置に戻ります。
そして明示的な位置が定義されていないFは既に配置されているアイテムと重ならない位置を探して配置されます。
おわりに
この記事ではグリッドレイアウトの自動配置アルゴリズムについてざっと見ていきました。
グリッドすごいですね!メリークリスマス!