実はそれほど難しくない、CSS Grid Layoutの使い方

2024年 2月26日 Posted 野々瀨(フロントエンドエンジニア)

IEのサポートが終了したことで、本格的にCSSのGrid Layoutが使用できるようになりました。もちろんIE11でも使えてはいましたが、プロパティ名が違っていたり、対応するプロパティが少なかったりと問題があり、IEではなかなか使いづらい感じでした(当時はSafariも使用することができず、対応に苦労した覚えがあります)

今回はそんなCSS Grid Layoutの使い方をご紹介しようと思います。

そもそもCSS Grid Layoutって?

CSS Grid Layout(以降はグリッドレイアウトと呼称)は2次元のレイアウトを簡単に表現ができる機能です。Excelのセルのようなもので、格子状に分割されたマス目に要素を配置してレイアウトしていきます。グリッドシステム(レイアウトグリッド)のような使い方で、ガイドに沿ってレイアウトを組むことができます。

また、Flexible Box Layoutと同じように、幅や高さをフレキシブルに表現することができます。

Flexible Box Layoutとの違い

Flexible Box Layout(以降はフレキシブルレイアウトと呼称)とは次のような違いがあります。

違いグリッドレイアウトフレキシブルレイアウト
配置方法 2次元 1次元
配置位置や方向 自由な位置に配置ができる 一方方向のみ
配置可能なアイテム数 決められた分割数の領域のみ 無制限
幅に応じた自動折り返し なし あり

配置方法

グリッドレイアウトは2次元であるのに対し、フレキシブルレイアウトは1次元です。

配置位置や方向

グリッドレイアウトは決められた領域内であれば自由な位置に配置ができるのに対し、フレキシブルレイアウトは水平方向か垂直方向のどちらか一方の方向での配置です。

幅に応じた自動折り返し

グリッドレイアウトは幅に応じてアイテムを自動的に折り返すことはありませんが、フレキシブルレイアウトは幅に応じてアイテムを自動的に折り返すことができます。

配置可能なアイテム数

グリッドレイアウトはあらかじめ決められた分割数の領域内で配置するのに対し、フレキシブルレイアウトは特に数の指定がなく無制限に並びます。ただし、グリッドレイアウトは暗黙的に列または行を分割(生成)する機能を持ち、自動的に配置しているときにアイテム数が行列数を超える場合、配置する方向にそって行や列を追加してくれます。暗黙的な分割については見出し「暗黙的な行列の分割(生成)」をご覧ください。

折り返し

グリッドレイアウトはグリッドに沿った配置を行うため折り返しする機能はありません。フレキシブルレイアウトは親要素の幅を基準に、はみ出した子の要素を折り返すことができます。ただし、グリッドレイアウトは「配置可能なアイテム数」でも説明したとおり、暗黙的な行列の追加により自動配置する機能を持ちますので、折り返されたように配置されることがあります。

基本構成

コンテナ

配置を行う基本となる領域が「コンテナ」です。これをベースにして要素を配置します。

アイテム

配置を行う要素そのものが「アイテム」です。コンテナ直下にある要素は全てアイテムとなります。つまり、配置した要素がある場合は、コンテナ直下にいる必要があります。

ライン

水平・垂直に分割した線が「ライン」です。基本的な軸は水平は左から右へ、垂直は上から下で、番号が1から振られています。

負の値として数えることもでき、軸が水平は右から左へ、垂直は下から上で-1から振られています。

トラック

行または列が「トラック」です。ラインとラインの間の領域のことを指します。

セル

アイテムを配置する一つのマス目が「セル」です。

エリア

アイテムを設置する範囲の領域が「エリア」です。範囲であることから、隣接したセル同士が結合された状態の領域や、一つのセルでもエリアとなります。

使い方

基本的な構成

まずHTMLを用意します。ここでは次のようにコンテナ要素とアイテム要素の簡単なものを用意しています。

<div class="container">
	<div class="item">A</div>
	<div class="item">B</div>
	<div class="item">C</div>
	<div class="item">D</div>
	<div class="item">E</div>
	<div class="item">F</div>
</div>

次にCSSを用意します。displayプロパティのgridをコンテナ要素へ指定します。

.container {
	display: grid;
}

これで最低限の記述は完了です。

次にトラックを確保します。トラックの確保は行をgrid-template-columnsプロパティ、列をgrid-template-rowsプロパティで行います。grid-template-rowsプロパティは分割したい行数分の高さを、スペースで区切って指定します。grid-template-columnsプロパティは分割したい列数分の幅を、スペースで区切って指定します。これらのプロパティを指定しますと、指定した分割数に応じてアイテムが自動的に配置されます。

このプロパティはコンテナに対して指定することで行えます。

例えば次のように指定しますと、2行3列のトラックを確保します。

.container {
	display: grid;
	grid-template-columns: 100px 150px 1fr;
	grid-template-rows: 50px 70px;
}

autoとfrの単位について

幅や高さ(長さ)はpxなど基本的な単位を使用することができますが、autofrという値も指定することができます。

autoはアイテムの長さに応じて自動的に調整されます。

frはコンテナの長さにおける残りの長さの占有率です。grid-template-columns: 100px 1fr 2fr 3fr;とした場合、1列目は100px、2列目~4列目は残りの長さを1 + 2 + 3 = 6分割し、その割合で割り当てられます。つまり2列目は残りの長さの6分の1の長さが割り当てられ、3列目は残りの長さの6分の2の長さが割り当てられ、4列目は残りの長さの6分の3の長さが割り当てられます。

.container {
	display: grid;
	grid-template-columns: 100px 1fr 2fr 3fr;
	grid-template-rows: auto auto;
}

幅や高さが連続して同じ値の時の一括指定

grid-template-columnsプロパティとgrid-template-rowsプロパティは、repeat関数を使用することで、幅や高さが連続して同じ値の時に一括指定することができます。構文はrepeat(繰り返す数, 値)と指定します。

.container {
	display: grid;
	grid-template-columns: 1fr repeat(2, 100px) auto;
	grid-template-rows: repeat(4, auto);
}

幅に応じたアイテムの埋め方

repeat関数の第一引数には幅に応じたアイテムの埋める方法を指定することもできます。auto-fillauto-fitという値を指定することができます。

auto-fillはアイテムが自動で配置された際にできた残りの余白に対して、新たなグリッドを生成します。

.container {
	display: grid;
	grid-template-columns: repeat(auto-fill, 70px);
}

また、minmax関数と組み合わせることで、残りの余白が最小値に満たした場合にグリッドを生成し、それ以外は最大値の幅としてアイテムの幅が設定されるようになります。

.container {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
}

auto-fitrepeat関数の第二引数で指定したアイテムの幅や高さが、親要素の幅や高さに収まるアイテムをその方向に並べ、収まらない場合は収まらない分の余白を残し、次の行または列に折り返して並びます。

.container {
	display: grid;
	grid-template-columns: repeat(auto-fit, 70px);
}

また、minmax関数と組み合わせることで、余白がアイテムの幅(最小値)を満たすまではアイテムの幅を最大値として表示し、それを超える場合は次の行または列に折り返して並びます。

.container {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(70px, 1fr));
}

ラインを基準にアイテムを配置

grid-rowプロパティまたはgrid-columnプロパティを使用しますと、ラインを基準にアイテムを配置することができます。grid-rowプロパティは行の位置、grid-columnプロパティは列の位置の指定を行います。それぞれのプロパティはアイテムに対して指定し、値は次のいずれかで指定します。

.item {
	/* grid-row */
	grid-row: 行の開始番号;

	grid-row: 行の開始番号 / 行の終了番号;

	/* grid-column */
	grid-column: 列の開始番号;

	grid-column: 列の開始番号 / 列の終了番号;
}

grid-rowプロパティはgrid-row-startプロパティとgrid-row-endプロパティのショートハンドで、grid-columnプロパティはgrid-column-startプロパティとgrid-column-endプロパティのショートハンドです。

.item {
	/* grid-row */
	grid-row: grid-row-start;

	grid-row: grid-row-start / grid-row-end;

	/* grid-column */
	grid-column: grid-column-start;

	grid-column: grid-column-start / grid-column-end;
}
.container {
	display: grid;
	grid-template-columns: repeat(3, 50px);
	grid-template-rows: repeat(4, 50px);
}

/* A */
.item:nth-child(1) {
	grid-row: 1;
}

/* B */
.item:nth-child(2) {
	grid-row: 1;
	grid-column: 2;
}

/* C */
.item:nth-child(3) {
	grid-row: 1 / 3;
	grid-column: 3;
}

/* D */
.item:nth-child(4) {
	grid-row: 2 / 4;
	grid-column: 1 / 3;
}

/* E */
.item:nth-child(5) {
	grid-row: 3;
}

/* F */
.item:nth-child(6) {
	grid-row: 4;
	grid-column: 2 / 4;
}

また、grid-row-endプロパティ部分やgrid-column-endプロパティ部分はspanキーワードを使用しますと、ライン番号ではなく範囲で指定することができます。例えば、span 3と指定すれば開始行または開始列から三つのセルということになります。

.container {
	display: grid;
	grid-template-columns: repeat(4, 50px);
	grid-template-rows: repeat(3, 50px);
}

.item {
	grid-row: 2 / 4;
	grid-column: 2 / span 3;
}

なお、grid-rowプロパティとgrid-columnプロパティはショートハンドgrid-areaプロパティがあります。値は次のいずれかで指定します。

.item {
	grid-area: grid-row-start;

	grid-area: grid-row-start / grid-column-start;

	grid-area: grid-row-start / grid-row-end / grid-column-start;

	grid-area: grid-row-start / grid-row-end / grid-column-start / grid-column-end;
}

エリアに名前を付けてアイテムを配置

grid-template-areasプロパティとgrid-areaプロパティを使用しますと、エリアに名前を付けてアイテムを配置することができます。grid-template-areasプロパティは領域を定義するために使用し、grid-areaプロパティは当てはめる領域を指定します。grid-template-areasプロパティはコンテナに対して指定し、grid-areaプロパティはアイテムに対して指定します。

grid-template-areasプロパティは値はセルをスペースで区切ってクォーテーションでくくったものを行とし、さらにスペースで区切ることで列とすることで指定します。grid-areaプロパティは当てはめる領域の名前を指定します。

<div class="container">
	<div class="header">Header</div>
	<div class="main">Main</div>
	<div class="side">Side</div>
	<div class="footer">Footer</div>
</div>
.container {
	display: grid;
	grid-template-areas:
		"header header"
		"main side"
		"footer side";
}

.header {
	grid-area: header;
}

.main {
	grid-area: main;
}

.side {
	grid-area: side;
}

.footer {
	grid-area: footer;
}

ラインに名前を付けて行列の大きさを設定

grid-template-columnsプロパティとgrid-template-rowsプロパティは、各ラインに対して名前を付けて指定することができます。名前を付けた後にgrid-rowプロパティやgrid-columnプロパティに割り当てたい名前を指定することで、その割り当てられた名前と一致する幅や高さが反映されます。

例えば四つの列のラインがあったとした場合、grid-template-columns: [line1] 1fr [line2] 2fr [line3] 3fr [line4]という感じで、ラインとトラックをスペースで交互に区切り、ラインは角括弧に付けたい名前を指定します。また、特に名前を付ける必要がなければ、grid-template-columns: [line1] 1fr 2fr [line3] 3frという感じで、名前の部分は何も指定せずに続けて値を指定します。

.container {
	display: grid;
	grid-template-columns: [aaa] 1fr [bbb] 2fr [ccc] 3fr [ddd];
	grid-template-rows: repeat(3, auto);
}

アイテムの整列

アイテムの整列は幾つかのプロパティがあります。

アイテムを一括で整列

アイテムを一括で整列することができます。水平(X軸)方向はjustify-itemsプロパティ、垂直(Y軸)方向はalign-itemsプロパティを使用します。それぞれのプロパティはコンテナに対して指定します。指定可能な値は次のとおりで、初期値はstretchです。

justify-itemsalign-items
start 左寄せ 上寄せ
center 左右中央寄せ 上下中央寄せ
end 右寄せ 下寄せ
stretch 両端寄せ 両端寄せ
baseline ベースライン寄せ ベースライン寄せ

justify-itemsプロパティの値の例:

align-itemsプロパティの値の例:

.container {
	display: grid;
	grid-template-columns: repeat(4, 50px);
	grid-template-rows: repeat(3, 50px);
	align-items: center;
	justify-items: end;
}

また、justify-itemsプロパティとalign-itemsプロパティはショートハンドとしてplace-itemsプロパティで一括指定ができます。place-itemsプロパティはplace-items: align-items justify-items;と指定できますが、place-items: center;のように一つ目の値だけを指定した場合、二つ目は一つ目と同じ値を指定したことと同じになります。

.container {
	display: grid;
	grid-template-columns: repeat(4, 50px);
	grid-template-rows: repeat(3, 50px);
	place-items: center end;
}

個別のアイテムを整列

個別にアイテムを整列することができます。水平(X軸)方向はjustify-selfプロパティ、垂直(Y軸)方向はalign-selfプロパティを使用します。それぞれのプロパティはコンテナに対して指定します。指定可能な値は次のとおりで、初期値はstretchです。

justify-selfalign-self
start 左寄せ 上寄せ
center 左右中央寄せ 上下中央寄せ
end 右寄せ 下寄せ
stretch 両端寄せ 両端寄せ
baseline ベースライン寄せ ベースライン寄せ
.container {
	display: grid;
	grid-template-columns: repeat(4, 50px);
	grid-template-rows: repeat(3, 50px);
}

.item:nth-child(2) {
	align-self: center;
}

.item:nth-child(3) {
	justify-self: end;
}

また、justify-selfプロパティとalign-selfプロパティはショートハンドとしてplace-selfプロパティで一括指定ができます。place-selfプロパティはplace-self: align-self justify-self;と指定できますが、place-self: center;のように一つ目の値だけを指定した場合、二つ目は一つ目と同じ値を指定したことと同じになります。

.item:nth-child(2) {
	place-self: center end;
}

全てのアイテム全体を整列

コンテナ要素を基準にアイテム全体を整列することができます。水平(X軸)方向はjustify-contentプロパティ、垂直(Y軸)方向はalign-contentプロパティを使用します。それぞれのプロパティはコンテナに対して指定します。指定可能な値は次のとおりで、初期値はstretchです。

justify-contentalign-content
start 左寄せ 上寄せ
center 左右中央寄せ 上下中央寄せ
end 右寄せ 下寄せ
stretch 両端寄せ 両端寄せ
space-between アイテム間の余白を均等 アイテム間の余白を均等
space-around 先端・終端を含めアイテム間の余白を均等
※ 先端と終端の余白はアイテム間の2分の1
先端・終端を含めアイテム間の余白を均等
※ 先端と終端の余白はアイテム間の2分の1

justify-contentプロパティの値の例:

align-contentプロパティの値の例:

.container {
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: repeat(2, auto);
	align-content: center;
	justify-content: end;
	width: 150px;
	height: 100px;
}

また、justify-contentプロパティとalign-contentプロパティはショートハンドとしてplace-contentプロパティで一括指定ができます。place-contentプロパティはplace-content: align-content justify-content;と指定できますが、place-content: center;のように一つ目の値だけを指定した場合、二つ目は一つ目と同じ値を指定したことと同じになります。

.container {
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: repeat(2, auto);
	place-content: center end;
	width: 150px;
	height: 100px;
}

アイテム間の余白(ガター)

gapプロパティを使用することで、アイテム間の余白(ガター)を調整することができます。gapプロパティはコンテナに対して指定し、値は次のいずれかで指定します。

.container {
	gap: 行列のアイテム間の余白;

	gap: 行のアイテム間の余白 列のアイテム間の余白
}

次のコードは行のアイテム間の余白を30px、列のアイテム間の余白を10pxあてています。

.container {
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: repeat(2, auto);
	gap: 30px 10px;
}

なおgapプロパティは、行のアイテム間の余白としてrow-gapプロパティ、列のアイテム間の余白としてcolumn-gapプロパティのショートハンドでもあります。

.container {
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: repeat(2, auto);
	row-gap: 30px;
	column-gap: 10px;
}

自動配置の方向

grid-auto-flowプロパティを使用することで、自動的にアイテムを配置する方法を指定することができます。grid-auto-flowプロパティはコンテナに対して指定し、値は次のいずれかで指定します。

説明
row 水平(Z字型)方向へ配置します。初期値です。
column 垂直(N字型)方向へ配置します。
dense 空間を埋めます。
row dense 水平(Z字型)方向かつ空間を埋めます。
column dense 垂直(逆N字型)方向かつ空間を埋めます。
.container {
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: repeat(3, auto);
	grid-auto-flow: column;
}

暗黙的に生成されたトラックの大きさを設定

grid-auto-rowsプロパティまたはgrid-auto-columnsプロパティを使用することで、暗黙的に生成されたトラックの大きさを設定することができます。コンテナに対して指定します。次の例では行に対して幅を50pxに固定しています。

.container {
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: auto;
	grid-auto-rows: 50px;
}

配置の定義を一括で指定

grid-templateプロパティはgrid-template-areaプロパティ、grid-template-rowsプロパティ、grid-template-columnsプロパティを一括で指定できるショートハンドです。例えば次のように置き換えることができます。

各プロパティによる指定:

.container {
	display: grid;
	grid-template-areas:
		"header header"
		"main side"
		"footer side";
	grid-template-columns: 1fr 200px;
	grid-template-rows: 50px 1fr auto;
}

grid-templateプロパティによる一括指定:

.container {
	display: grid;
	grid-template:
		"header header" 50px
		"main side" 1fr
		"footer side" auto / 1fr 200px;
}

暗黙的な行列の分割(生成)

グリッドレイアウトは暗黙的に行または列を分割(生成)する機能があります。アイテムの配置と任意に指定しなければ、配置する方向に沿ってアイテムを自動的に並べていきます。このとき、例えば列だけ分割数を設定した場合、アイテムが列の数を超えた場合、自動的に次の行を生成し、その生成した行にアイテムを配置していきます。

<div class="container">
	<div class="item">A</div>
	<div class="item">B</div>
	<div class="item">C</div>
	<div class="item">D</div>
	<div class="item">E</div>
	<div class="item">F</div>
	<div class="item">G</div>
</div>
.container {
	display: grid;
	grid-template-columns: repeat(3, auto);
	grid-template-rows: auto;
}

応用編

グリッドシステム(レイアウトグリッド)

応用というわけではないですが、BootstrapなどのCSSフレームワークにもあります、グリッドシステムとして使用することができます。プロトタイピングツールなどデザインではレイアウトグリッドと同じ使い方が可能です。

.container {
	display: grid;
	grid-template-columns: repeat(12, auto);
	gap: 10px;
}

.item {
	background-color: #eee;
}

.item.col-4 {
	grid-column-start: span 4;
}

.item.col-8 {
	grid-column-start: span 8;
}
<div class="container">
	<div class="item col-4">A</div>
	<div class="item col-8">B</div>
</div>

レイヤー数の少ない表組みや定義リストなどの縦ぞろえ

定義リストでdt要素とdd要素が水平方向に並び、この1セットを垂直方向に並ぶような実装を行うときにグリッドレイアウトが役立ちます。例えば次の例では、フォームの項目名と入力欄が垂直方向にそろったように並びます。

<dl class="list">
	<dt><label for="name">お名前</label></dt>
	<dt><input name="name" id="name"></dt>
	<dt><label for="mail_address">メールアドレス</label></dt>
	<dt><input name="mail_address" id="mail_address"></dt>
	<dt><label for="content">お問い合わせ内容</label></dt>
	<dt><textarea name="content" id="content"></textarea></dt>
</dl>
.list {
	display: grid;
	grid-template-columns: auto 1fr;
	gap: 10px;
}

grid-template-rowsプロパティを指定しなくても暗黙的に行を生成してくれますで、行が増えても問題なく配置されます。

水平方向に並んでいるパーツの中の要素の高さをそろえる

カード型コンポーネントなど、水平方向に並んでいるパーツで、それぞれの中の要素の垂直方向の位置をそろえる場合、CSSだけではなかなか実装が難しいことがあります。実装しようとしますと、テーブルレイアウトで組むか、高さを完全に固定するか、JavaScriptを用いて同じ水平方向の要素の最大の高さに合わせて高さを固定するといった方法で実装する必要があります。

そこで、グリッドレイアウトのサブグリッドという機能を使用しますと、簡単に実装することができます。

<div class="cards">
	<div class="card">
		<img src="xxx.png" width="208" height="117" alt="">
		<p class="title">タイトルタイトルタイトルタイトルタイトルタイトルタイトルタイトルタイトル</p>
		<p class="detail">説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章</p>
		<a href="xxx">詳細ページへ</a>
	</div>
	<div class="card">
		<img src="xxx.png" width="208" height="117" alt="">
		<p class="title">タイトルタイトルタイトルタイトル</p>
		<p class="detail">説明文章説明文章説明文章説明文章説明文章説明文章</p>
		<a href="xxx">詳細ページへ</a>
	</div>
	<div class="card">
		<img src="xxx.png" width="208" height="117" alt="">
		<p class="title">タイトル</p>
		<p class="detail">説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章</p>
		<a href="xxx">詳細ページへ</a>
	</div>
</div>
.cards {
	display: grid;
	grid-template-columns: repeat(3, 208px);
	grid-template-rows: repeat(4, auto);
	column-gap: 20px;
}

.card {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 4;
}

また、サブグリッドは階層が深くてもサブグリッドとして扱うことができます。

<div class="cards">
	<div class="card">
		<img src="xxx.png" width="208" height="117" alt="">
		<div class="text">
			<p class="title">タイトルタイトルタイトルタイトルタイトルタイトルタイトルタイトルタイトル</p>
			<p class="detail">説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章</p>
		</div>
		<a href="xxx">詳細ページへ</a>
	</div>
	<div class="card">
		<img src="xxx.png" width="208" height="117" alt="">
		<div class="text">
			<p class="title">タイトルタイトルタイトルタイトル</p>
			<p class="detail">説明文章説明文章説明文章説明文章説明文章説明文章</p>
		</div>
		<a href="xxx">詳細ページへ</a>
	</div>
	<div class="card">
		<img src="xxx.png" width="208" height="117" alt="">
		<div class="text">
			<p class="title">タイトル</p>
			<p class="detail">説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章説明文章</p>
		</div>
		<a href="xxx">詳細ページへ</a>
	</div>
</div>
.cards {
	display: grid;
	grid-template-columns: repeat(3, 208px);
	grid-template-rows: repeat(4, auto);
	column-gap: 20px;
}

.card {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: span 4;
}

.card .text {
	display: grid;
	grid-template-rows: subgrid;
	grid-row: 2 / span 2;
}

ただし、サブグリッドについてはFirefoxは比較的前のバージョンから対応されていましたが、それ以外のブラウザーは最近実装したばかりだったり、そもそも対応していなかったりと標準で実装するには難しいかもしれません。詳しい対応ブラウザーとバージョンについては次のページをご覧ください。https://caniuse.com/css-subgrid

なお、アイテムにdisplay: contentsを指定することでも同じようなことを実装することは可能です。こちらはアイテムが自動的に配置される(流れる)方向を変えてあげる必要があります。また、アイテムのボックスモデルがコンテンツだけになりますので、アイテムでのmarginプロパティやborderプロパティなど、コンテンツ以外の調整を行うことはできなくなります。

.cards {
	display: grid;
	grid-template-columns: repeat(3, 208px);
	grid-template-rows: repeat(4, auto);
	grid-auto-flow: column;
	column-gap: 20px;
}

.card {
	display: contents;
}

関連リンク