本文へスキップ
スキルアップカレッジ

CSS Grid——2 次元のレイアウトを組む

レッスン4:CSS Grid——2 次元のレイアウトを組む

このレッスンで学ぶこと

  • Flexbox との使い分け(1 次元と 2 次元)を理解する
  • 行と列を定義する基本的な書き方を身につける
  • 領域に名前を付けてレイアウトを組む方法を理解する
  • 既存のレイアウト技術と CSS Grid を併用する考え方を身につける

前回のレッスンでは、CSS カスタムプロパティで値を一元管理する方法を学びました。今回は、配置そのものを一元的に組み立てる CSS Grid を扱います。要素を並べるという点ではこれまでに学んだレイアウト技術と目的が重なりますが、得意な場面が異なります。

1 次元と 2 次元——Flexbox と Grid の役割分担

Flexbox は、要素を「1 つの方向」に並べて整えることが得意なレイアウトの仕組みでした。横一列に並べる、縦一列に積む、といった単一方向の並びが、その技術の主戦場です。

一方、CSS Grid は「行と列」という 2 つの方向を同時に扱えるレイアウトの仕組みです。ダッシュボードのように、縦にも横にも意味のある区切りがあるレイアウトを組むときに向いています。

flowchart TD
  A[レイアウトを組みたい] --> B{並びの方向は1つで足りるか}
  B -->|1方向でよい| C[1次元向けの技術が向いている]
  B -->|行と列の両方を制御したい| D[Gridが向いている]

この図は、レイアウトを組む前に考えるべき最初の分岐点を表しています。ナビゲーションのリンクを横に並べる、カードの中の要素を縦に積むといった場面は、多くの場合、既存の 1 方向の並びを整える技術だけで完結します。ページ全体の骨格のように、行と列の両方を同時に意識したいときに Grid の出番が来ます。

📝 補足 すでに学んだレイアウトの基本操作(display: flex、justify-content、align-items、gap など)は本レッスンで繰り返しません。ここで扱うのは、1 方向の並びに強い技術と Grid のどちらを選ぶかという判断と、Grid 独自の書き方です。

display: grid ではじめる

Grid も、これまでに学んだレイアウト技術と同じように、親要素に指定を書くことから始まります。

.layout {
  display: grid;
  grid-template-columns: 200px 1fr;
  gap: 16px;
}
<div class="layout">
  <aside>サイドバー</aside>
  <main>本文</main>
</div>

grid-template-columns は、列の数と幅をまとめて定義するプロパティです。この例では「200px の列」と「残りをすべて使う列」の 2 列が定義され、子要素は書かれた順にそれぞれの列へ自動的に配置されます。

fr 単位——余った幅を分け合う

1fr の fr は「fraction(割合)」を表す Grid 専用の単位です。fr は、余った幅をほかの列との比率で分け合うという意味を持ちます。

.layout {
  display: grid;
  grid-template-columns: 1fr 2fr 1fr;
}

この指定では、全体の幅を「1:2:1」の比率で 3 列に分けます。真ん中の列が両端の 2 倍の幅になります。

.layout {
  display: grid;
  grid-template-columns: 200px 1fr 1fr;
}

固定幅の列と fr の列を混ぜることもできます。この例では、最初の列は常に 200px を保ち、残りの幅を 2 つの列が均等に分け合います。パーセント指定と違い、余白(gap)を差し引いたあとの幅から比率が計算されるため、余白を含めた計算を自分で行う必要がありません。

💡 ポイント fr は「残りをどう分けるか」だけを表す単位です。列の合計が画面の何 % を占めるかを自分で計算する必要がなく、比率だけを指定すればブラウザ側が残りの幅を割り振ってくれます。

行の定義と、繰り返しの書き方

列と同じように、grid-template-rows で行の高さも定義できます。

.layout {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 80px auto 60px;
  gap: 16px;
}

この例では、1 行目を 80px、3 行目を 60px に固定し、2 行目は中身の量に応じて自動的に高さが決まる auto を指定しています。ヘッダーとフッターの高さを固定し、本文の高さだけを可変にしたいときによく使う組み合わせです。

同じ幅の列をいくつも並べたいときは、repeat() という関数で繰り返しを表現できます。

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
}

repeat(3, 1fr) は 1fr 1fr 1fr と同じ意味です。列の数が増えるほど、繰り返しの記法のほうが読みやすくなります。

repeat() は、列の幅に上限と下限を指定する minmax() と組み合わせることもできます。

.gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
}

minmax(160px, 1fr) は「最小 160px、最大は余白を分け合った幅」という意味です。auto-fit は、その幅の条件を満たす範囲で、入る列の数を自動的に決める指定です。コンテナの幅に収まる分だけ列が並び、収まらなくなった要素は自動的に次の行へ送られます。列数を固定値で書かずに、コンテナの幅から逆算させる書き方です。

💡 ポイント repeat(auto-fit, minmax(...)) は、画面幅の条件を自分で書かなくても、コンテナの幅に応じて列数が自動的に変わる点が特徴です。Grid が持つ独自の計算の仕組みによるもので、Flexbox の折り返し(flex-wrap)とは動き方が異なります。折り返しは「収まらなくなったら次の行へ送る」という判断ですが、auto-fit はあらかじめ収まる列数を計算してから配置する点が異なります。

領域に名前を付ける——grid-template-areas

Grid には、行と列の交差点に名前を付けて、レイアウトを図のように書ける機能があります。grid-template-areas です。

.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header"
    "sidebar main"
    "footer footer";
  gap: 16px;
}

.page__header { grid-area: header; }
.page__sidebar { grid-area: sidebar; }
.page__main { grid-area: main; }
.page__footer { grid-area: footer; }
<div class="page">
  <header class="page__header">ヘッダー</header>
  <aside class="page__sidebar">サイドバー</aside>
  <main class="page__main">本文</main>
  <footer class="page__footer">フッター</footer>
</div>

grid-template-areas の中の文字列は、実際のレイアウトの見取り図になっています。同じ名前を横に並べれば、その領域が横に結合されます。header が 1 行目の両方の列にまたがっているのは、"header header" という書き方がそのまま反映された結果です。各子要素は grid-area に領域名を指定するだけで、複雑な行番号や列番号を数える必要がありません。

領域を使わずに位置を指定したい場合は、grid-column と grid-row で開始位置と終了位置を数値で指定することもできます。

.page__header {
  grid-column: 1 / 3;
  grid-row: 1 / 2;
}

1 / 3 は「1 番目の線から 3 番目の線まで」という意味で、結果として 1 列目と 2 列目の両方にまたがります。数値による指定は柔軟ですが、行や列の数が増えるほど、どの数字がどこを指すのかを追いにくくなります。

📖 もっと詳しく 領域に名前を付ける書き方は、CSS だけを見てもページの骨格が想像しやすいという利点があります。数値で範囲を指定する書き方も存在しますが、ページ全体の骨格を組むときは、名前付きの領域のほうが見通しがよくなることが多いです。細かい調整だけ数値指定を使い、骨格全体は名前付きの領域で組むという併用も可能です。

収まりきらない要素はどうなるか——暗黙のグリッド

grid-template-columns や grid-template-rows で定義した行と列を「明示的なグリッド」と呼びます。では、あらかじめ定義した行数を超える要素が入ってきたら、どうなるのでしょうか。

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 120px;
  gap: 12px;
}
<div class="gallery">
  <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>

この例では、行の高さを明示的に定義していません。4 番目以降の要素は、定義していない新しい行に自動的に配置されます。この自動的に生成される行のことを「暗黙のグリッド」と呼びます。grid-auto-rows は、この暗黙のグリッドの行の高さを指定するプロパティです。指定しなければ、中身の量に応じた高さが自動的に割り当てられます。

同じように、列方向にあふれた場合の挙動を制御する grid-auto-flow というプロパティもあります。

.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-flow: row; /* 既定値。行方向に要素を送る */
}

grid-auto-flow: row は既定の挙動で、収まりきらない要素を次の行へ送ります。column を指定すると、逆に列方向へ要素を送るようになりますが、あらかじめ行の数を決めておく必要があるため、使う場面は限られます。多くのレイアウトでは既定値のままで十分です。

⚠️ 注意 明示的なグリッドと暗黙のグリッドの違いを意識しておくと、「指定していないはずの行に要素が並んでいる」という状況に出会ったときに、原因を切り分けやすくなります。あふれた要素は消えているのではなく、暗黙のグリッドに自動的に配置されています。

既存のレイアウト技術との併用

実際の制作では、Grid か、これまで学んだ 1 方向の並びに強い技術か、どちらか一方だけを使うのではなく、両方を組み合わせることがほとんどです。ページ全体の骨格を Grid で組み、その内側の細かい並びは 1 方向の並びに強い技術で整えるという役割分担が代表的な組み合わせ方です。

.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "header header"
    "sidebar main";
}

.page__header {
  grid-area: header;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
<header class="page__header">
  <div class="logo">サイト名</div>
  <nav>ナビゲーション</nav>
</header>

.page は Grid でページ全体の骨格を組み、.page__header の内部はロゴとナビゲーションを横並びに整えるレイアウト技術として振る舞っています。1 つの要素が同時に「Grid の子要素」と「1 方向の並びを整える技術の親要素」という 2 つの役割を持つことは、まったく問題ありません。

判断の目安は次のとおりです。

  • ページ全体・セクション全体の骨格:行と列の両方を意識するなら Grid
  • 部品の内部・要素どうしの並び:1 方向の並びで説明が付くなら Flexbox

どちらか一方に統一しようとするより、レイアウトの単位ごとに向いているほうを選ぶという発想のほうが、無理のない組み方につながります。

.card-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

.card {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

この例では、カードを並べる外側の並びを Grid が担当し、カード 1 枚の中の要素(見出し・本文・ボタンなど)を縦に積む役割を内側の技術が担当しています。外側は「行と列」、内側は「1 方向」という、それぞれの得意分野に沿った役割分担です。

🔰 初学者の方へ 「このレイアウトは Grid で組むべきか、それとも 1 方向の並びに強い技術で組むべきか」と迷ったら、いま組もうとしている範囲が「行と列、両方に意味があるか」を自問してください。両方に意味があれば Grid、片方の方向だけで説明が付くならもう一方の技術という基準で、多くの場面を判断できます。

実習:ページの骨格を Grid で組む

次の条件を満たすページの骨格を、grid-template-areas を使って組んでみましょう。

  • ヘッダーは幅いっぱいに表示する
  • サイドバーは左側に 220px 幅で固定する
  • 本文はサイドバーの右側の残り幅すべてを使う
  • フッターは幅いっぱいに表示する
<div class="page">
  <header class="page__header">ヘッダー</header>
  <aside class="page__sidebar">サイドバー</aside>
  <main class="page__main">本文</main>
  <footer class="page__footer">フッター</footer>
</div>

手順は次のとおりです。

  1. .page に display: grid を指定する
  2. grid-template-columns で「220px と 1fr」の 2 列を定義する
  3. grid-template-areas で、ヘッダーとフッターが両方の列にまたがるレイアウトを文字列で表現する
  4. 各子要素に grid-area で対応する領域名を指定する
  5. 余裕があれば、サイドバーの内部にロゴと簡単なメニューを用意し、内側の並びに 1 方向の並びに強い技術を組み合わせてみる
  6. ブラウザで表示し、サイドバーと本文の幅が意図どおりになっているかを確認する

まとめ

このレッスンでは、以下のことを学びました。

  • Flexbox は 1 次元、Grid は 2 次元のレイアウトに向いていること
  • display: grid と grid-template-columns で列を定義できること
  • fr 単位は余った幅を比率で分け合うための Grid 専用の単位であること
  • repeat() や minmax() を使うと、繰り返しの列や、幅に応じて自動調整される列を表現できること
  • grid-template-areas を使うと、CSS の中にレイアウトの見取り図をそのまま書けること
  • ページ全体の骨格には Grid、部品内部の並びには 1 方向の並びに強い技術という役割分担が組みやすいこと

前半の 4 レッスンで、CSS が壊れる原因の整理、名前の付け方、値の一元管理、配置の技術という土台が揃いました。次のレッスンからは、これらを組み合わせて部品として設計する「コンポーネント指向」に進みます。


確認クイズ

このレッスンの理解度をチェックしましょう。