- CSS
CSS Gridの使い方|repeat・fr・minmaxとコピペ用レイアウト集
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期連続の黒字決算。