カードを横に3つ並べたい、写真を格子状にきれいに敷き詰めたい——そんなレイアウトを少ないコードで実現できるのが CSS の Grid(グリッド)レイアウトです。この記事では、display: grid と grid-template-columns / grid-template-rows を使って格子状のレイアウトを組む基本から、gap・fr 単位・repeat() 関数、さらに minmax() と repeat(auto-fit, …) によるレスポンシブな自動折り返しまでを、動くデモを交えて初心者の方にも分かるように解説します。Flexbox との使い分けにも触れるので、「どっちを使えばいいの?」という疑問もすっきりするはずです。
目次
Grid レイアウトとは何か
Grid レイアウトは、要素を行と列からなる格子(グリッド)に沿って配置するための仕組みです。あらかじめ「何列・何行のマス目を作るか」を決めておき、その上に子要素を並べていくイメージです。表計算ソフトのセルのように、縦と横の両方向を同時に指定してレイアウトを組めるのが最大の特長です。
使い方はシンプルで、親要素に display: grid を指定し、列の構成を grid-template-columns、行の構成を grid-template-rows で定義します。すると、その直下の子要素が自動的にマス目へ順番に流し込まれていきます。子要素ひとつひとつに細かい指定をしなくても、親側のルールだけで全体が整列するのが扱いやすいところです。
基本の使い方
まずは最小の例です。子要素を持つ親要素に display: grid を付け、grid-template-columns で列の幅を半角スペース区切りで並べます。値の個数がそのまま列の数になります。次の例は、200px・200px・200px の3列を作る指定です。
.grid {
display: grid;
/* 200px の列を 3 つ並べる(= 3 カラム) */
grid-template-columns: 200px 200px 200px;
}
この親要素の中に div を6個入れると、上の行に3個、その下の行に3個と、自動的に折り返して2行のグリッドができあがります。行については grid-template-rows で明示しなくても、子要素の中身に応じて高さが自動で決まります。逆に、各行の高さを固定したい場合は次のように行も指定できます。
.grid {
display: grid;
grid-template-columns: 200px 200px 200px; /* 3 列 */
grid-template-rows: 100px 100px; /* 2 行(各 100px) */
}
fr 単位と repeat() で柔軟に列を作る
列幅をすべて px で固定すると、画面幅が変わったときに余白が中途半端になったり、はみ出したりしがちです。そこで便利なのが Grid 専用の fr 単位です。fr は「fraction(割合)」の略で、余っているスペースをどんな比率で分け合うかを表します。
たとえば grid-template-columns: 1fr 1fr 1fr と書くと、利用できる横幅を1:1:1で3等分した3列になります。画面幅が広がれば各列も比例して広がるため、レスポンシブに強いレイアウトになります。比率を変えたいときは 2fr 1fr のように書けば、左を右の2倍の幅にできます。
同じ指定を何度も並べるのは面倒なので、繰り返しをまとめる repeat() 関数が用意されています。repeat(回数, 値) の形で書き、repeat(3, 1fr) は 1fr 1fr 1fr とまったく同じ意味になります。列数が多いほど効果的です。
/* どちらも「均等な 3 カラム」で同じ意味 */
.grid-a {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
}
.grid-b {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 1fr を 3 回繰り返す */
}
/* px と fr は混在できる。サイドバー固定+本文は残り全部 */
.layout {
display: grid;
grid-template-columns: 240px 1fr;
}
最後の例のように、px と fr は混ぜて使えます。「サイドバーは240pxで固定、本文は残りの幅をすべて使う」といったレイアウトが 240px 1fr の一行で実現できます。
gap でセルのすき間をあける
カードを並べるときには、セル同士に適度なすき間が欲しくなります。Grid では gap プロパティで、行と列のすき間をまとめて指定できます。margin と違って外側には余白が付かず、セルとセルの間にだけすき間が入るため、端がそろってきれいに収まります。値を2つ書くと行・列を別々に指定できます。
| プロパティ | 意味 |
|---|---|
gap: 16px; | 行と列の両方のすき間を 16px にする(一括指定)。 |
gap: 24px 16px; | 行のすき間 24px、列のすき間 16px と別々に指定する。 |
row-gap: 24px; | 行(縦方向)のすき間だけを指定する。 |
column-gap: 16px; | 列(横方向)のすき間だけを指定する。 |
動く例で確認する
ここまでの内容をまとめた、もっとも基本的なグリッドのデモです。display: grid と grid-template-columns: repeat(3, 1fr) で均等な3カラムを作り、gap でセルのすき間をあけています。CSSタブを開いて、列の指定とすき間の関係を確認してみてください。番号付きのセルが、上の行から順に左→右へ流し込まれている様子が分かります。
<div class="grid">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
.grid {
display: grid;
/* 横幅を 3 等分した 3 カラム。1fr が「余りの 1 つ分」 */
grid-template-columns: repeat(3, 1fr);
/* セル同士のすき間(行・列の両方に 16px) */
gap: 16px;
padding: 24px;
background-color: #f3f4f6;
font-family: sans-serif;
}
.item {
display: flex;
align-items: center;
justify-content: center;
height: 80px;
background-color: #3b82f6;
color: #fff;
font-size: 24px;
font-weight: bold;
border-radius: 8px;
}
minmax() と auto-fit でレスポンシブに折り返す
repeat(3, 1fr) のように列数を固定すると、画面が狭いスマートフォンでは1列が窮屈になってしまいます。本来はメディアクエリで列数を切り替えるところですが、Grid にはメディアクエリなしで自動的に折り返す書き方があります。それが repeat(auto-fit, minmax(…)) です。
鍵になるのが minmax(最小値, 最大値) 関数です。これは「列幅を最小値より小さくせず、最大値までは伸ばす」という幅の範囲を表します。minmax(160px, 1fr) なら「最低でも160pxは確保し、余りがあれば1frとして広げる」という意味になります。
これを repeat() の回数の代わりに auto-fit と組み合わせます。auto-fit は「入るだけの列を自動で作る」というキーワードです。結果として、コンテナの幅に応じて列数が自動で増減し、狭くなれば自然に折り返すグリッドができます。よく使う定番の一行なので、丸ごと覚えてしまうと便利です。
.cards {
display: grid;
/* 最小 160px・最大 1fr の列を、入るだけ自動で並べる */
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 16px;
}
下のデモで、プレビュー領域の幅を変えてみてください(ブラウザのウィンドウ幅を狭めると分かりやすいです)。幅が広いときは横に多くのカードが並び、狭くなると列数が減って自動的に折り返します。メディアクエリを1行も書いていないのに、レスポンシブに振る舞っているのがポイントです。
<div class="grid">
<div class="card">Card 1</div>
<div class="card">Card 2</div>
<div class="card">Card 3</div>
<div class="card">Card 4</div>
<div class="card">Card 5</div>
</div>
.grid {
display: grid;
/*
最小 160px・最大 1fr のカラムを、入るだけ自動で並べる。
プレビューの幅を狭めると、列数が自動で減って折り返します。
*/
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 16px;
padding: 24px;
background-color: #f3f4f6;
font-family: sans-serif;
}
.card {
display: flex;
align-items: center;
justify-content: center;
height: 100px;
background-color: #10b981;
color: #fff;
font-size: 18px;
font-weight: bold;
border-radius: 8px;
}
Grid と Flexbox の使い分け
「Grid と Flexbox はどちらを使えばいいのか」は、よく出る疑問です。ざっくりした目安は、2次元(行と列の両方)を同時に制御したいなら Grid、1次元(横一列、または縦一列)の並びなら Flexboxです。
たとえば、カードを縦横の格子状に敷き詰めるギャラリーやダッシュボードのような全体レイアウトは Grid が得意です。一方、ナビゲーションメニューのように要素を横一列に並べてすき間を調整する、ボタンとアイコンを横並びにして中央そろえにする、といった一方向の配置は Flexbox の方が手軽です。実際の制作では「ページ全体の骨組みは Grid、その中の細かいパーツは Flexbox」のように併用するのが一般的です。なお、要素をマス目の中で上下・左右にそろえる justify-items や align-items といった整列系のプロパティもありますが、これらは別の記事で扱うため、本記事では割愛します。
グリッドが思った通りに並ばない原因
Grid は強力ですが、「列が増えない」「すき間が効かない」といったつまずきもよくあります。代表的な原因を順に確認していきましょう。
display: grid を親要素に付け忘れている
もっとも多いのがこれです。grid-template-columns や gap は、display: grid が指定された要素(グリッドコンテナ)に対してのみ効果を持ちます。display: grid を書き忘れると、grid-template-columns を指定しても何も起こりません。また、これらのプロパティを並べたい子要素自身ではなく、それらを囲む親要素に付けるという点も間違えやすいので注意してください。
直接の子要素しかグリッドに並ばない
グリッドのマス目に配置されるのは、コンテナの直接の子要素だけです。子要素をさらに div などで一段くるんでしまうと、その内側の要素はグリッドの対象になりません。思った数のセルが並ばないときは、HTML の入れ子構造を見直し、並べたい要素がコンテナの直下にあるか確認しましょう。
列幅の合計がコンテナ幅を超えている
grid-template-columns: 300px 300px 300px のように px で固定した列幅の合計が、親要素の幅や画面幅を超えると、はみ出してレイアウトが崩れます。固定幅が必要な場面もありますが、横並びを画面幅に追従させたいときは fr 単位や minmax() を使い、幅に余裕を持たせるのが安全です。とくにスマートフォンを考えると、1fr や auto-fit ベースで組んでおくと崩れにくくなります。
まとめ
Grid レイアウトは、親要素に display: grid を付け、grid-template-columns で列を、必要なら grid-template-rows で行を定義するだけで、格子状のレイアウトを手軽に組める仕組みです。列幅には余りを比率で分け合う fr 単位が便利で、repeat(3, 1fr) のように repeat() でまとめれば均等カラムも一行です。セルのすき間は gap で整え、repeat(auto-fit, minmax(160px, 1fr)) を使えばメディアクエリなしで自動折り返しのレスポンシブグリッドが作れます。2次元のレイアウトは Grid、1次元の並びは Flexbox という使い分けを意識しつつ、まずは3カラムのデモを真似て、自分のページに置き換えてみてください。