CSS Grid(グリッドレイアウト)とは、親要素に display: grid を指定し、grid-template-columns などで「列と行のます目」を定義して、子要素をそのます目に並べるレイアウト方法です。 横と縦を同時に決められるので、カード一覧やページ全体の骨組みを、少ないCSSで組めます。

この記事では、コンテナ側・アイテム側のプロパティを1枚にまとめた早見表から始め、fr・auto・minmax・auto-fit・auto-fill の幅の決まり方を同じHTMLで比べた測定結果、メディアクエリなしのカード一覧・聖杯レイアウト・サイドバー付き・中央寄せなどのコピペ用コード、subgrid でカード内の高さをそろえる例、はみ出しを minmax(0, 1fr) で直す例を扱います。数値はすべて Chromium 141.0.7390.37(Playwright のヘッドレス実行)で測ったものです。

CSS Gridプロパティの早見表

コンテナ側(親要素)に書くプロパティ

プロパティ 役割 よく使う値の例
display グリッドにする grid/inline-grid
grid-template-columns 列の幅と数を決める 1fr 1fr 1fr/repeat(3, 1fr)/200px 1fr
grid-template-rows 行の高さと数を決める auto 1fr auto/repeat(2, 100px)/subgrid
grid-template-areas エリアに名前を付けて配置を決める "header header" "nav main"
gap ます目の間隔(行 列の順) 16px/24px 16px
row-gap/column-gap 行間・列間を個別に指定 24px
grid-auto-rows 自動で増える行の高さ minmax(100px, auto)
grid-auto-columns 自動で増える列の幅 200px
grid-auto-flow 自動配置の向きと詰め方 row(既定)/column/dense
justify-items ます目の中での横位置(全アイテム) stretch(既定)/start/center/end
align-items ます目の中での縦位置(全アイテム) stretch(既定)/start/center/end
place-items align-items と justify-items の一括指定 center
justify-content ます目全体の横位置(余白があるとき) start(既定)/center/space-between
align-content ます目全体の縦位置(余白があるとき) start(既定)/center/end
place-content align-content と justify-content の一括指定 center

アイテム側(子要素)に書くプロパティ

プロパティ 役割 よく使う値の例
grid-column 列方向の位置と幅 1 / 3/span 2/1 / -1(端から端まで)
grid-row 行方向の位置と高さ 1 / 3/span 3
grid-area エリア名、または行と列の一括指定 header/1 / 1 / 3 / 2
justify-self 自分だけ横位置を変える start/center/end
align-self 自分だけ縦位置を変える start/center/end
place-self align-self と justify-self の一括指定 center/end start
order 自動配置の順番を変える -1/1
z-index 重ねたときの前後 1
min-width はみ出し対策 0

幅の指定に使う値と関数

値・関数 意味
px・%・rem など 固定の長さ。% はコンテナの幅に対する割合
fr 残りの幅を分け合う比率(fraction)
auto 中身に合わせ、余りがあれば広がる
min-content/max-content 中身の最小幅/折り返さないときの幅
minmax(最小, 最大) 幅の下限と上限を決める
repeat(回数, 幅) 同じ指定の繰り返し
repeat(auto-fit, …)/repeat(auto-fill, …) 入るだけ列を作る(回数を自動で決める)
fit-content(上限) 中身に合わせるが、上限を超えない

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CSS Gridの使い方|repeat・fr・minmaxとコピペ用レイアウト集」

CSS Gridの基本:3ステップで並べる

1. 親に display: grid、2. 列を決める、3. 間隔を決める

<div class="grid">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>
.grid {
  display: grid;                          /* 1. グリッドにする */
  grid-template-columns: repeat(3, 1fr);  /* 2. 同じ幅の列を3つ作る */
  gap: 16px;                              /* 3. ます目の間隔 */
}

これだけで、6つの子要素が3列×2行に並びます。子要素に何も書かなくても、左上から順にます目へ自動で入っていくのが Grid の基本の動きです。行は指定していませんが、子要素の数に合わせて自動で増えます(この自動で増えた行を「暗黙の行」と呼びます)。

用語の整理

用語 意味
グリッドコンテナ display: grid を指定した親要素
グリッドアイテム コンテナの直接の子要素
グリッドライン ます目を区切る線。左端・上端から 1, 2, 3 … と番号が付く
トラック ラインとラインの間の列または行
セル 列と行が交わる1ます
エリア 複数のセルをまとめた長方形の範囲

3列のグリッドなら、縦のラインは 1〜4 の4本です。右端から数える場合は -1, -2 … と負の番号が使えます。

FlexboxとGridの使い分けの目安

作りたいもの 向いている方法
横一列に並べて、中身の幅に合わせたい(ナビ、ボタン群) Flexbox
列の幅をそろえて、縦横に並べたい(カード一覧、ギャラリー) Grid
ページ全体の骨組み(ヘッダー・サイドバー・本文・フッター) Grid
要素を重ねたい Grid(同じセルに置く)

Flexbox は「中身が幅を決める」、Grid は「親が決めたます目に中身を入れる」と考えると選びやすくなります。

fr・auto・minmaxの幅の決まり方を比べる

同じHTMLで列の指定だけを変えた測定結果

幅 900px のコンテナに、中身の長さが違う3つのアイテム(「A」「BBBBBBBBBB BBBBBBBBBB」「C」)を入れ、grid-template-columns だけを変えて、それぞれの幅を測りました(gap は 0)。

<div class="g">
  <div>A</div>
  <div>BBBBBBBBBB BBBBBBBBBB</div>
  <div>C</div>
</div>
.g {
  display: grid;
  width: 900px;
  grid-template-columns: 1fr 1fr 1fr; /* ここを変える */
}
grid-template-columns の値 1列目 2列目 3列目 決まり方
1fr 1fr 1fr 300 300 300 全体を3等分
1fr 2fr 1fr 225 450 225 1:2:1 に分ける
200px 1fr 1fr 200 350 350 固定幅を引いた残り 700px を2等分
25% 1fr 2fr 225 225 450 25% を引いた残り 675px を 1:2
auto auto auto 230.6 437.8 231.5 中身の幅+余りを均等に上乗せ
200px auto 1fr 200 217.9 482.1 auto は中身の幅、残りは fr が受け取る
auto 1fr auto 10.7 877.8 11.6 両端は中身の幅、中央が残り全部
min-content max-content 1fr 10.7 217.9 671.4 最小幅・折り返さない幅・残り

(単位は px。小数は使われたフォントでの文字幅によるものです)

この表からわかる fr と auto の違い は次のとおりです。

  • fr は「残りの幅」を比率で分けます。 固定幅(px・%)や auto の列が先に幅を取り、その残りを fr の列が分け合います。
  • auto は中身の幅が基準です。 fr の列が一緒にあるときは中身の幅ぴったり(10.7px、217.9px)になり、fr の列がないときは余った幅を auto 同士で均等に上乗せします(auto auto auto の行)。
  • 「両端は中身に合わせ、中央を伸ばす」は auto 1fr auto と書けます。ヘッダーの「ロゴ・ナビ・ボタン」の並びによく合います。

gapがあるときは % より fr が扱いやすい

3等分を 33.333% で書くと、gap の分だけ全体がコンテナからはみ出します。gap を 20px にして測った結果です。

指定 各列の幅 コンテナの scrollWidth(幅 900px)
repeat(3, 1fr) + gap: 20px 286.7 900(収まる)
repeat(3, 33.333%) + gap: 20px 300 940(40px はみ出す)

fr は gap を引いた後の残りを分けるので、間隔を変えても計算し直す必要がありません。

repeat():同じ指定を繰り返す

/* この2つは同じ意味 */
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-columns: repeat(4, 1fr);

/* パターンの繰り返しも書ける(100px 1fr 100px 1fr) */
grid-template-columns: repeat(2, 100px 1fr);

/* 一部だけ repeat にする */
grid-template-columns: 200px repeat(3, 1fr);

minmax():幅の下限と上限を決める

minmax(最小, 最大) は、「これより狭くならず、これより広くならない」という範囲を指定します。

/* 200px より狭くならず、余りがあれば広がる */
grid-template-columns: minmax(200px, 1fr) 1fr;

/* 行の高さ:最低 100px、中身が多ければ伸びる */
grid-auto-rows: minmax(100px, auto);
書き方 意味 使いどころ
minmax(200px, 1fr) 最小 200px、残りを分け合う カード一覧の列
minmax(0, 1fr) 最小 0、残りを分け合う はみ出し対策(後述)
minmax(100px, auto) 最小 100px、中身に合わせて伸びる 行の高さ
minmax(200px, 25%) 200px〜コンテナの25% サイドバーの幅

auto-fitとauto-fillの違いを測定結果で比べる

入るだけ列を作る:repeat(auto-fit / auto-fill, minmax())

列の数を決めずに、「1列は最小 200px、入るだけ並べる」と指定できます。メディアクエリを書かなくても、幅に応じて列数が変わるのが利点です。

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

auto-fit と auto-fill は、アイテムが1行に満たないときに違いが出ます。コンテナの幅とアイテムの数を変えて測りました(gap は 0)。「列の定義」は getComputedStyle() で読んだ grid-template-columns の値です。

アイテム数 コンテナ幅 キーワード アイテム1つの幅 列の定義(計算値)
2 1000px auto-fit 500 500px 500px 0px 0px 0px
2 1000px auto-fill 200 200px 200px 200px 200px 200px
3 1000px auto-fit 333.3 333.3px ×3、0px ×2
3 1000px auto-fill 200 200px ×5
6 1000px auto-fit 200 200px ×5(2行)
6 1000px auto-fill 200 200px ×5(2行)
3 700px どちらも 233.3 233.3px ×3
3 450px どちらも 225 225px ×2(2行)
  • auto-fit:空の列を幅 0 につぶし、ある分のアイテムが横いっぱいに広がります。
  • auto-fill:空の列も幅を保ったまま残し、アイテムの幅は列数ぶんで割った幅のままです。
  • アイテムが1行を埋めるだけあれば(6個の行)、どちらも同じ結果になります。
選ぶ基準 向いているほう
アイテムが少ないときも横幅いっぱいに広げたい auto-fit
アイテムが少なくても、ほかの行と同じカード幅を保ちたい auto-fill

最小幅より狭い画面でのはみ出しと min() での対処

minmax(200px, 1fr) は「200px より狭くならない」という指定なので、コンテナが 200px より狭いと、列がコンテナからはみ出します。

指定 コンテナ幅 アイテムの幅 コンテナの scrollWidth
repeat(auto-fit, minmax(200px, 1fr)) 180px 200 200(20px はみ出す)
repeat(auto-fit, minmax(min(200px, 100%), 1fr)) 180px 180 180(収まる)

最小値を min(200px, 100%) にすると、コンテナが狭いときは 100% が選ばれ、はみ出しがなくなります。最小幅を大きめ(300px など)にするときは、この書き方にしておくと小さな画面でも収まります。

アイテムの配置:ライン番号・span・エリア名

ライン番号と span で位置を決める

子要素は自動で並びますが、grid-column と grid-row で位置や大きさを指定できます。

.g {
  display: grid;
  width: 400px;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 50px;
}
#a { grid-column: 1 / -1; }      /* 1本目のラインから最後のラインまで(全幅) */
#b { grid-column: 2 / 4; }       /* 2本目から4本目まで(2列分) */
#c { grid-area: 2 / 1 / 4 / 2; } /* 行開始 / 列開始 / 行終了 / 列終了 */

測った位置と大きさ(左, 上, 幅, 高さ)は次のとおりです。

要素 指定 左 上 幅 高さ
#a grid-column: 1 / -1 0 0 400 50
#b grid-column: 2 / 4 100 50 200 50
#c grid-area: 2 / 1 / 4 / 2 0 50 100 100
書き方 意味
grid-column: 1 / 3 1本目のラインから3本目のラインまで(2列分)
grid-column: span 2 いまの位置から2列分
grid-column: 2 / span 2 2本目のラインから2列分
grid-column: 1 / -1 左端から右端まで
grid-row: span 3 3行分

1 / -1 は「端から端まで」の定番です。列数が変わってもそのまま使えます。ただし、-1 が指すのは grid-template-columns で定義した列の端で、自動で増えた列は含みません。

grid-auto-flow: dense ですき間を詰める

幅の違うアイテムが混ざると、入りきらなかったアイテムが次の行に送られ、すき間ができることがあります。grid-auto-flow: dense を指定すると、後ろの小さなアイテムがすき間に入ります。

3列のグリッドに「2列分・2列分・1列分・1列分」の4つを並べて測った位置(左, 上)です。

アイテム 既定(row) dense
1つ目(2列分) 0, 0 0, 0
2つ目(2列分) 0, 50 0, 50
3つ目(1列分) 200, 50 200, 0(1行目のすき間に入る)
4つ目(1列分) 0, 100 200, 50

dense を使うと 見た目の順番がHTMLの順番と変わります。 キーボードでの移動や読み上げの順番はHTMLの順なので、順番に意味のない一覧(写真のギャラリーなど)に向いています。

エリア名で配置する:grid-template-areas

配置を「図」のように書けるのが grid-template-areas です。

.page {
  display: grid;
  grid-template-columns: 200px 1fr;
  grid-template-areas:
    "header header"
    "nav    main"
    "footer footer";
}
.page > header { grid-area: header; }
.page > nav    { grid-area: nav; }
.page > main   { grid-area: main; }
.page > footer { grid-area: footer; }
ルール 内容
1行を1つの文字列で書く 行の数だけ "..." を並べる
同じ名前を並べると結合される "header header" で2列分
空けたいセルは . "nav . main"
エリアは長方形だけ L字形などは指定できない
各行の列数をそろえる 数が合わないと指定全体が無効になる

メディアクエリで grid-template-areas だけを書き換えれば、HTMLを変えずに並び順を切り替えられます(次の章の聖杯レイアウトで使います)。

コピペ用レイアウト集

ここからは、そのまま貼って使える定番レイアウトです。測定結果を添えたものは、Chromium 141 で位置と大きさを確認しています。

1. メディアクエリなしのカード一覧

<div class="cards">
  <article class="card">カード1</article>
  <article class="card">カード2</article>
  <article class="card">カード3</article>
  <article class="card">カード4</article>
  <article class="card">カード5</article>
  <article class="card">カード6</article>
</div>
.cards {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.card {
  padding: 16px;
  border: 1px solid #ccc;
  border-radius: 8px;
}

カード6枚で、画面幅を変えて測った結果です。

画面幅 列数 カード1枚の幅 行数
1200px 4 288 2
800px 3 256 2
520px 2 252 3
320px 1 320 6

「最小 240px」という1つの数字だけで、4列から1列まで切り替わっています。

2. 聖杯レイアウト(ヘッダー・3カラム・フッター)

ヘッダーとフッターが全幅、その間に「ナビ・本文・補足」の3列が並ぶ、聖杯レイアウト(Holy Grail Layout)と呼ばれる形です。

<div class="page">
  <header>ヘッダー</header>
  <nav>ナビ</nav>
  <main>本文</main>
  <aside>補足</aside>
  <footer>フッター</footer>
</div>
body { margin: 0; }

.page {
  display: grid;
  min-height: 100vh;
  gap: 16px;
  grid-template-columns: 200px minmax(0, 1fr) 200px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header header"
    "nav    main   aside"
    "footer footer footer";
}
.page > header { grid-area: header; }
.page > nav    { grid-area: nav; }
.page > main   { grid-area: main; }
.page > aside  { grid-area: aside; }
.page > footer { grid-area: footer; }

/* 768px 以下では1列にして、本文をナビより先に表示する */
@media (max-width: 768px) {
  .page {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto 1fr auto auto auto;
    grid-template-areas:
      "header"
      "main"
      "nav"
      "aside"
      "footer";
  }
}

測った位置と大きさです(表示領域の高さ 800px、各要素の中身は1行)。

要素 幅1200px:左, 上, 幅, 高さ 幅600px:左, 上, 幅, 高さ
header 0, 0, 1200, 24 0, 0, 600, 24
nav 0, 40, 200, 720 0, 696, 600, 24
main 216, 40, 768, 720 0, 40, 600, 640
aside 1000, 40, 200, 720 0, 736, 600, 24
footer 0, 776, 1200, 24 0, 776, 600, 24

grid-template-rows: auto 1fr auto によって、中身が少なくてもフッターが画面の下端(776px〜800px)に付きます。 幅 600px では、HTMLの順番は nav → main のままですが、表示は main が先になっています。

3. サイドバー付き2カラム

.layout {
  display: grid;
  gap: 24px;
  grid-template-columns: minmax(200px, 25%) minmax(0, 1fr);
}

幅 1000px のコンテナで、サイドバーが 250px(25%)、本文が 726px(残りから gap 24px を引いた幅)になりました。サイドバーは 25% を基本にしつつ、200px より狭くなりません。本文側を minmax(0, 1fr) にしているのは、長いコードや表が入ってもレイアウトが広がらないようにするためです(理由は後述します)。

固定幅のサイドバーなら、もっと短く書けます。

.layout {
  display: grid;
  gap: 24px;
  grid-template-columns: 240px minmax(0, 1fr);
}

4. 上下左右の中央寄せ

.center {
  display: grid;
  place-items: center;
  min-height: 100vh; /* 高さが必要。親に高さがなければ縦には寄らない */
}

600×400px のコンテナに 100×50px の要素を入れて測ると、左 250px・上 175px で、上下左右とも中央になりました。2行で済む中央寄せとして覚えておくと便利です。

書き方 寄せる対象
place-items: center 各セルの中でアイテムを中央に
place-content: center ます目全体をコンテナの中央に
place-self: center(アイテム側) そのアイテムだけ中央に

place-content の動きも確かめました。600×300px のコンテナに 100px×2列・50px×2行のます目(gap 10px)を作り、justify-content: center; align-content: end; を指定すると、ます目全体が横は中央(左端 195px)、縦は下端(上端 190px)に寄りました。

5. フッターを下端に付ける(ページ全体)

body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  grid-template-rows: auto 1fr auto;
}
<body>
  <header>ヘッダー</header>
  <main>本文</main>
  <footer>フッター</footer>
</body>

表示領域 800px で、header が 0〜24px、main が 24〜776px、footer が 776〜800px になりました。本文が短いページでも、フッターが画面の下端に付きます。

6. 画像の上に文字を重ねる

Grid では、複数のアイテムを同じセルに置くと重なります。position: absolute を使わずに重ねられるので、親の高さが中身に合わせて決まります。

<div class="hero">
  <img src="photo.jpg" alt="">
  <p class="hero-caption">キャプション</p>
</div>
.hero {
  display: grid;
}
.hero > * {
  grid-area: 1 / 1;  /* 全員を1行目・1列目に置く */
}
.hero img {
  display: block;
  width: 100%;
  height: 300px;
  object-fit: cover;
}
.hero-caption {
  place-self: end start; /* 縦は下、横は左 */
  margin: 0;
  padding: 16px;
  color: #fff;
}

幅 600px・高さ 300px の画像に対して、キャプションは上端 244px・高さ 56px(下端がちょうど 300px)で、画像の左下に重なりました。親の .hero の高さも 300px で、画像の高さに合っています。

7. 一部だけ大きいタイル(ギャラリー)

.tiles {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  grid-auto-rows: 120px;
  grid-auto-flow: dense;
}
.tiles .wide { grid-column: span 2; }
.tiles .tall { grid-row: span 2; }
.tiles .big  { grid-column: span 2; grid-row: span 2; }

幅 520px のコンテナに「big・通常・wide・tall・通常・通常」の6つを入れて測ると、4列(1列 124px)になり、5つ目のタイルが1行目の右端のすき間(左 396px・上 0px)に、6つ目が tall の右隣(左 132px・上 256px)に入りました。dense によって、HTMLの順番より前の位置のすき間が埋まっています。

subgridでカード内の高さをそろえる

カードごとに見出しの高さが違うとずれる

カードの中に「見出し・本文・リンク」があるとき、見出しの行数がカードごとに違うと、本文やリンクの位置がそろいません。カードの高さ自体は Grid がそろえてくれますが、カードの中身の行は、カードごとに別々に決まるためです。

<div class="cards">
  <article class="card">
    <h3>短い題名</h3>
    <p>本文は1行です。</p>
    <a href="#">詳しく見る</a>
  </article>
  <article class="card">
    <h3>とても長い題名が入ると2行以上に折り返されるカード</h3>
    <p>本文もやや長めで、複数行に折り返されることを想定した文章が入ります。さらに続きます。</p>
    <a href="#">詳しく見る</a>
  </article>
  <article class="card">
    <h3>中くらいの題名のカード</h3>
    <p>本文。</p>
    <a href="#">詳しく見る</a>
  </article>
</div>

subgrid:親の行を、カードの中身でも使う

grid-template-rows: subgrid を指定すると、カードは自分で行を作らず、親グリッドの行をそのまま使います。 カードが親の3行分を占めるように grid-row: span 3 を合わせて指定します。

.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}
.card {
  display: grid;
  grid-template-rows: subgrid; /* 親の行を使う */
  grid-row: span 3;            /* 見出し・本文・リンクの3行分 */
  gap: 8px;                    /* カード内の行間(親の gap を上書き) */
  padding: 16px;
  border: 1px solid #ccc;
}
.card h3,
.card p {
  margin: 0;
}

幅 720px のコンテナで、3枚のカードの「見出しの高さ」「本文の上端」「リンクの上端」を測りました。

方法 カード 見出しの高さ 本文の上端 リンクの上端
subgrid なし(カードごとに display: grid) 1枚目 27 52 84
2枚目 81 106 210
3枚目 54 79 111
subgrid あり 1枚目 81 106 210
2枚目 81 106 210
3枚目 81 106 210
Flexbox + リンクに margin-top: auto 1枚目 27 52 210
2枚目 81 106 210
3枚目 54 79 210
  • subgrid ありでは、3枚とも見出しの行が 81px(いちばん長い見出しの高さ)にそろい、本文とリンクの開始位置も一致しています。
  • Flexbox で margin-top: auto を使う方法は、リンクの位置(210)はそろいますが、本文の開始位置(52・106・79)はそろいません。
  • 「いちばん下の要素だけそろえばよい」なら Flexbox、「途中の行もそろえたい」なら subgrid、と使い分けられます。

auto-fit と組み合わせる

列数が変わるカード一覧でも、subgrid は使えます。カード4枚を次のCSSで並べ、画面幅を変えて測りました。

.cards {
  display: grid;
  gap: 24px 16px;
  grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr));
}
.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  gap: 8px;
  padding: 16px;
  border: 1px solid #ccc;
  border-radius: 8px;
}
.card h3, .card p { margin: 0; }
.card a { align-self: end; }
画面幅 並び 1段目のカードの見出しの高さ 2段目のカードの見出しの高さ
800px 3枚+1枚 84(3枚とも) 28
560px 2枚+2枚 84(2枚とも) 28(2枚とも)

高さがそろうのは「同じ段に並んだカード同士」です。段ごとに親の行が3行ずつ作られ、それぞれの段でいちばん高い中身に合わせて行の高さが決まります。

subgrid を使うときのポイント

ポイント 内容
grid-row: span N を合わせて書く カードが親の何行分を使うかを決める。N はカード内の要素数に合わせる
親には行の定義を書かなくてよい 自動で増える行(暗黙の行)がそのまま使われる
gap は親から引き継がれる カード内の間隔を変えたいときは、カード側で gap を上書きする
subgrid にした方向には行が自動で増えない 要素が N 個を超えると最後の行に重なる。要素数と N をそろえる
列にも使える grid-template-columns: subgrid で、フォームのラベルと入力欄の列をそろえられる

gap の引き継ぎ、行が自動で増えない点、ライン名が引き継がれる点は、MDN の Subgrid のページで確認しました。同ページの対応状況は「Baseline Widely available(2023年9月から利用可能)」という表示です。Chromium 141 でも CSS.supports('grid-template-rows', 'subgrid') は true でした。

はみ出しをminmax(0, 1fr)で直す

1fr は「中身より狭くならない」

1fr 1fr と書けば必ず半分ずつになる、と考えがちですが、1fr は minmax(auto, 1fr) と同じ意味で、最小値が auto(中身の最小幅)です。折り返せない長い中身が入ると、その列は中身の幅まで広がり、コンテナからはみ出します。

幅 600px の2列グリッドの左側に、長い1行のコード(<pre>)を入れて測りました。

<div class="g">
  <div><pre>const veryLongVariableName = someFunction(argumentNumberOne, argumentNumberTwo, argumentNumberThree);</pre></div>
  <div>side</div>
</div>
.g {
  display: grid;
  width: 600px;
  grid-template-columns: 1fr 1fr; /* ここを変える */
}
pre {
  margin: 0;
  overflow: auto;
}
指定 1列目 2列目 コンテナの scrollWidth 結果
1fr 1fr 973 29 1002 402px はみ出す。pre の overflow: auto も働かない
minmax(0, 1fr) minmax(0, 1fr) 300 300 600 半分ずつ。pre の中で横スクロールできる
1fr 1fr + アイテムに min-width: 0 300 300 600 同上

直す前は、1列目が中身の幅(973px)まで広がり、2列目は 29px に押しつぶされています。<pre> に overflow: auto を付けていても、列そのものが広がってしまうため、スクロールバーは出ません。

直し方は2通り

/* 直し方1:列の最小値を 0 にする */
.g {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  /* repeat(2, minmax(0, 1fr)) でも同じ */
}

/* 直し方2:アイテムの最小幅を 0 にする */
.g > * {
  min-width: 0;
}

どちらも測定結果は同じ(300px ずつ)でした。列の定義を見ただけで意図が伝わる minmax(0, 1fr) のほうが、あとから読む人にわかりやすい書き方です。

長いURL・省略表示にも同じ対処が必要

スペースのない長いURLでも同じことが起きます。幅 400px の2列グリッドで測りました。

指定 1列目 2列目 scrollWidth
1fr 1fr 663 29 692(はみ出す)
repeat(2, minmax(0, 1fr)) + overflow-wrap: anywhere 200 200 400(列の中で折り返す)
repeat(2, minmax(0, 1fr)) + 省略表示(下のCSS) 200 200 400(…で省略される)
/* 1行で省略表示にする */
.title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

text-overflow: ellipsis が効かないときも、原因は列が中身の幅まで広がっていることが多く、minmax(0, 1fr) で解決します。

はみ出す中身 対処
<pre>・長いコード 列を minmax(0, 1fr) に。pre に overflow: auto
長いURL・英数字の連続 列を minmax(0, 1fr) に。中身に overflow-wrap: anywhere
省略表示にしたい見出し 列を minmax(0, 1fr) に。中身に省略表示の3行
幅の広い表 列を minmax(0, 1fr) に。表を overflow-x: auto の要素で包む
大きな画像 画像に max-width: 100%; height: auto;

思ったとおりにならないときの確認表

症状 原因 直し方
列が等分にならない/横にはみ出す 1fr の最小値が中身の幅 minmax(0, 1fr) にする
gap を付けたらはみ出した 列を % で指定している fr に変える
小さな画面ではみ出す minmax(240px, 1fr) の最小幅がコンテナより大きい minmax(min(240px, 100%), 1fr) にする
アイテムが少ないと間延びする auto-fit で空の列がつぶれ、広がっている auto-fill にする
place-items: center で縦に寄らない コンテナに高さがない min-height を指定する
grid-template-areas が効かない 各行の列数が違う/エリアが長方形でない 列数をそろえ、長方形にする
孫要素が並ばない グリッドアイテムになるのは直接の子だけ 子も display: grid にするか、subgrid を使う
子の width: 100% が狭い % の基準はコンテナではなく、置かれたセル 列の定義側で幅を決める
1 / -1 で端まで届かない -1 は定義した列の端。自動で増えた列は含まない grid-template-columns で列を定義する
subgrid で中身が重なる 要素数が span N の N より多い N を要素数に合わせる
行の高さがばらばら 自動で増える行は中身の高さになる grid-auto-rows: 1fr や minmax(100px, auto) を指定する

よくある質問

Q. fr と % はどう使い分けますか?

A. gap を使うなら fr が扱いやすい単位です。fr は gap や固定幅を引いた残りを分け合うので、合計がコンテナの幅に収まります。% はコンテナの幅そのものに対する割合なので、gap を足すと合計が100%を超えます(今回の測定では 3列×33.333%+gap 20px で 40px はみ出しました)。

Q. auto-fit と auto-fill はどちらを使えばよいですか?

A. アイテムが1行を埋めるだけあれば、どちらも同じ結果になります。違いが出るのはアイテムが少ないときで、横いっぱいに広げたいなら auto-fit、カードの幅をそろえたまま左に詰めたいなら auto-fill です。

Q. subgrid はどのブラウザで使えますか?

A. MDN の Subgrid のページでは「Baseline Widely available(2023年9月から利用可能)」と表示されており、主要ブラウザの現行版で使えます。対応していない環境も対象にする場合は、@supports (grid-template-rows: subgrid) { ... } の中に書き、外側には Flexbox と margin-top: auto などの代わりの指定を置く方法があります。

Q. Grid のます目を目で確認する方法はありますか?

A. Chrome や Firefox の開発者ツールで、要素の一覧に表示される「grid」のバッジをクリックすると、ライン番号・エリア名・gap が画面に重ねて表示されます。思った位置に入らないときは、まずこの表示でライン番号を確かめると原因が見つけやすくなります。

まとめ

  • Grid は「親に display: grid、grid-template-columns で列、gap で間隔」の3つで始められます。子要素は自動でます目に入ります。
  • fr は残りの幅を比率で分け、auto は中身の幅が基準です。gap があるときは % より fr が収まりやすくなります。
  • メディアクエリなしのカード一覧は repeat(auto-fit, minmax(min(240px, 100%), 1fr))。アイテムが少ないとき、auto-fit は広がり、auto-fill は幅を保ちます。
  • ページの骨組みは grid-template-areas、フッターを下端に付けるのは grid-template-rows: auto 1fr auto、中央寄せは place-items: center です。
  • カード内の見出し・本文・リンクの位置は grid-template-rows: subgrid と grid-row: span 3 でそろいます。
  • 1fr は中身より狭くならないので、長いコードやURLではみ出すときは minmax(0, 1fr) に変えます。
  • 端から端までは grid-column: 1 / -1、重ねるときは同じセルに grid-area: 1 / 1 です。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CSS Gridの使い方|repeat・fr・minmaxとコピペ用レイアウト集」

株式会社RJCでは、CSS Gridを使ったレイアウトの組み方を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?