CSS Grid でこの数年に本当に加わったのは subgrid(2023年9月にBaseline入り)です。これで「カード内の見出しと本文の高さを、カードをまたいで揃える」という、長年CSSだけでは解けなかった問題が解決しました。
この記事は2024年の公開後、2026年9月に見直しました。 公開当時に載せていた subgrid のコードは、親グリッドのトラックにまたがっていないため機能しません。動く形に修正したうえで、実務で使うパターンを追記しています。
subgridは、その要素が親グリッドの複数トラックにまたがっているときにだけ意味を持ちます。grid-column: span 2などの指定が必須ですgrid-template-areasやminmax()は Grid の初期(2017年)からある機能で、新しい追加ではありません
スポンサーリンク
CSS Grid が Flexbox と違うところ
結論から言うと、2方向を同時に揃えたいなら Grid、1方向に並べるだけなら Flexboxです。
| 観点 | CSS Grid | Flexbox |
|---|---|---|
| 次元 | 行と列を同時に制御する | 1方向(行 または 列) |
| サイズの決まり方 | 親がトラックを決める | 子の内容が幅を決める |
| 向く用途 | ページ全体の骨格、カード一覧、表組み | ナビゲーション、ボタン列、中央寄せ |
| 視覚的な定義 | grid-template-areas |
なし |
「どちらが優れているか」ではなく、揃えたい方向が2つあるかどうかで選びます。
subgrid:入れ子の要素を親のトラックに揃える
これが Grid の最大の弱点を埋めた機能です。 Grid が揃えられるのは直下の子要素だけで、その内側は関係のない別グリッドになっていました。
解決したい問題
カード一覧で、タイトルの行数がカードごとに違うと、本文の開始位置がバラバラになります。
<ul class="cards">
<li class="card">
<h3>短いタイトル</h3>
<p>本文...</p>
<a href="#">詳しく見る</a>
</li>
<li class="card">
<h3>こちらは2行に折り返す長めのタイトル</h3>
<p>本文...</p>
<a href="#">詳しく見る</a>
</li>
</ul>
subgrid で解く
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
/* 見出し・本文・リンクの3行を親側で定義する */
grid-template-rows: auto 1fr auto;
gap: 24px;
}
.card {
display: grid;
/* 親の3行にまたがる。この指定が無いと subgrid は効かない */
grid-row: span 3;
grid-template-rows: subgrid;
gap: 8px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 12px;
}
これですべてのカードで、本文の開始位置とリンクの位置が揃います。タイトルが1行のカードでも2行のカードでも、行の高さは一列で共有されます。
間違えやすい点
次のコードは動きません。 公開当時に載せていたのがこの形でした。
/* .child は1列しか占めていないので、継承するトラックが無い */
.parent {
display: grid;
grid-template-columns: 1fr 2fr;
}
.child {
display: grid;
grid-template-columns: subgrid;
}
/* 親の全列にまたがらせる */
.parent {
display: grid;
grid-template-columns: 1fr 2fr;
}
.child {
grid-column: 1 / -1; /* ← これが必要 */
display: grid;
grid-template-columns: subgrid;
}
覚え方はシンプルで、「またがっていないものは、継承するものが無い」です。
gap とライン名の扱い
gapは親の値を引き継ぎます。変えたい場合は子側でrow-gap/column-gapを上書きします- 親で付けたライン名は子からも使えます。子側で名前を追加することも可能です
- 2方向とも
subgridにすると、暗黙のトラックが作られなくなります。子要素がトラック数を超えると最後の行に詰め込まれるので、行方向はgrid-auto-rowsを使う方が安全な場合があります
対応は Firefox 71、Safari 16、Chrome/Edge 117 以降で、2023年9月に Baseline 入りしています。
スポンサーリンク
実務で使うレスポンシブの型
メディアクエリを書かずにカード一覧を組むのが、Grid で最も使う書き方です。
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: 24px;
}
これだけで、画面幅に応じて列数が自動で増減します。ブレークポイントを1つも書く必要がありません。
ただし、このコードには弱点があります
コンテナの幅が 260px を下回ると、はみ出します。 minmax() の最小値は「これ以上小さくしない」という指定だからです。
.cards {
display: grid;
/* コンテナが狭いときは 100% を使う */
grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
gap: 24px;
}
min(260px, 100%) にしておくと、狭い画面でも横スクロールが発生しません。この1行を入れるかどうかで、スマートフォンでの見え方が変わります。
auto-fit と auto-fill の違い
auto-fit |
auto-fill |
|
|---|---|---|
| アイテムが少ないとき | 空トラックを潰し、アイテムが広がる | 空トラックを残し、アイテムは元の幅のまま |
| 向く用途 | 常に横幅を使い切りたい | グリッドの位置を揃えたい |
アイテムが1つしかないときにそれが画面いっぱいに広がってほしくないなら auto-fillです。多くの場合は auto-fit で問題ありません。
grid-template-areas:レイアウトを図で書く
ページの骨格を組むときは、名前でエリアを指定すると読みやすくなります。
.layout {
display: grid;
grid-template-areas:
"header header"
"main sidebar"
"footer footer";
grid-template-columns: 1fr 280px;
grid-template-rows: auto 1fr auto;
min-height: 100dvh;
gap: 24px;
}
.layout > header { grid-area: header; }
.layout > main { grid-area: main; }
.layout > aside { grid-area: sidebar; }
.layout > footer { grid-area: footer; }
@media (width < 768px) {
.layout {
grid-template-areas:
"header"
"main"
"sidebar"
"footer";
grid-template-columns: 1fr;
}
}
スマートフォン用の記述で、エリア名を並べ替えるだけでレイアウトが変わります。 HTMLは1行も触りません。
注意点:見た目の順序とDOMの順序がずれると、キーボード操作の順序が崩れます。 上の例のようにサイドバーを本文の後ろに回す程度なら問題ありませんが、大きく入れ替えるのは避けてください。
スポンサーリンク
知っておくと便利な指定
要素を重ねる
同じセルに複数の要素を置くと重なります。position: absolute を使わずに重ね合わせができます。
.hero {
display: grid;
}
.hero > * {
grid-area: 1 / 1; /* 全部を1行1列目に置く */
}
.hero__text {
place-self: center;
z-index: 1;
}
親の高さが画像に追従するので、position: relative と高さ指定の組み合わせより扱いやすいです。
アイテムを最後まで伸ばす
.full-bleed {
grid-column: 1 / -1; /* -1 は「最後のライン」 */
}
暗黙の行の高さを決める
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-auto-rows: minmax(120px, auto); /* 追加される行の高さ */
}
grid-template-rows で定義していない行が作られたときの高さです。アイテム数が可変のときは、これを書いておかないと行の高さがバラつきます。
コンテナクエリと組み合わせる
Grid で組んだコンポーネントは、置かれる場所によって最適な列数が変わります。画面幅ではなく親要素の幅で分岐したい場合は、コンテナクエリを使います。
.card-area {
container-type: inline-size;
}
.card {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
@container (width >= 400px) {
.card {
grid-template-columns: 120px 1fr;
}
}
これで同じカードが、サイドバーでは縦積み、メイン領域では横並びになります。詳しくは@mediaルールの強化で可能になる新しいデザインにまとめました。
デバッグの方法
Grid はブラウザの開発者ツールに専用の表示があります。使わずに数値を勘で調整するのは遠回りです。
- Chrome / Edge:要素に付く「grid」バッジをクリックすると、トラック番号と
gapが線で表示されます - Firefox:レイアウトパネルの「グリッド」で、エリア名やライン番号を重ねて表示できます。
grid-template-areasのデバッグはFirefoxが優秀です
思った位置に来ないときは、まずトラック番号を可視化してください。 ほとんどの場合、ライン番号の数え間違いです(ラインは1から始まり、3列なら1〜4まであります)。
まとめ
subgridは親のトラックにまたがっているときだけ機能する。grid-row: span 3などが必須- カード一覧で見出しと本文の位置を揃えるのが最も実用的な使い道
gapは親から引き継がれる。ライン名も使える- レスポンシブの型は
repeat(auto-fit, minmax(min(260px, 100%), 1fr))。min()を入れないと狭い画面ではみ出す auto-fitは空トラックを潰し、auto-fillは残す- 重ね合わせは全要素に
grid-area: 1 / 1 - アイテム数が可変なら
grid-auto-rowsを指定する - コンポーネント単位の分岐は
@mediaではなく@container grid-template-areasとminmax()は2017年からある機能
余白の入れ方についてはgapプロパティでflexboxやgridに余白を入れる方法にまとめています。