CSS Gridの実務:minmaxとauto-fitで崩れない2次元レイアウト
grid-template-columns、fr、minmax、auto-fitでレスポンシブを最小コードで。Flexboxとの使い分けとはみ出し対策まで、コピペで動く例で解説。
PCで完璧に並んでいたカード3枚。スマホで開いたら、真ん中の1枚だけ文字がはみ出して横スクロールが出ていました。
原因を探したら、CSSに grid-template-columns: repeat(3, 1fr) と書いてありました。「3列」とハードコードしていたんです。360px幅でも律儀に3列を守ろうとして、カードが潰れ、長い英単語がはみ出していたわけです。
僕はこれを直すのに、メディアクエリを3つ書き足しました。768px用、480px用、360px用。今思えば完全に遠回りでした。repeat(auto-fit, minmax(...)) を1行知っていれば、メディアクエリはゼロで済んだんです。
CSS Gridは、覚える順番を間違えると「呪文の暗記」になります。逆に、効く道具を4つだけ先に押さえると、レイアウトの8割は崩れなくなります。今日はその4つに絞って話します。
この記事の要点
- Gridは2次元(縦横を同時に組む)、Flexboxは1次元(一方向に並べる)。ページの骨格とカード一覧はGrid、ボタンの中やナビの横並びはFlexbox。
- レスポンシブの主役は
repeat(auto-fit, minmax(280px, 1fr))。メディアクエリを書かずに列数が画面幅に追従する。 frは「余りを分け合う」単位。minmax(最小, 最大)は「ここまでは縮んでいい/ここまで伸びていい」の宣言。- はみ出しの9割は
min-width: 0で止まる。Gridアイテムは初期状態で中身より小さくならないので、これで「縮んでよし」と許可する。 - 画面全体の配置は
grid-template-areasで名前を付けると、スマホでの並べ替えがCSSだけで終わる。
まずGridとFlexboxの線引きから
ここが曖昧なまま書き始めると、後で必ずCSSが読めなくなります。判断軸は1つだけです。並べたい方向が1本か、縦横の両方か。
| 状況 | 向いている | 理由 |
|---|---|---|
| ボタン内のアイコン+文字 | Flexbox | 横一列だけ。1次元 |
| ナビゲーションの横並び | Flexbox | 一方向に流す |
| カード一覧(折り返す) | Grid | 行と列を同時に管理 |
| ヘッダー+サイド+本文+フッター | Grid | 画面の骨格は2次元 |
| カード内の「見出し・本文・ボタン」の縦積み | どちらでも | 1次元ならFlexbox、ボタンを下端に揃えたいならGrid |
僕の経験則はシンプルで、「折り返したい」「縦も横も揃えたい」と思った瞬間にGridです。1本の線に並べて終わりならFlexboxで十分。Flexboxの細かい使い分けはFlexboxパターンに、レイアウト全体の考え方はレスポンシブデザインにまとめてあるので、迷ったら行き来してください。
両方を敵対させる必要はありません。ページの骨格はGrid、その中の細かい部品はFlexbox。この入れ子が、いちばん素直に書けます。
fr と minmax を、お小遣いで理解する
専門用語が2つ出てきますが、難しくありません。身近な例えで一気に片付けます。
fr は「fraction(分け前)」の略です。お小遣いを兄弟で分ける場面を想像してください。1fr 1fr は「半分こ」。2fr 1fr は「兄が2、弟が1の比率で分ける」。余ったスペースを、指定した比率で配るのが fr です。px のような固定値と違って、画面が広がれば取り分も自動で増えます。
minmax(最小, 最大) は「縮んでいい下限と、伸びていい上限」の宣言です。minmax(200px, 1fr) なら、「200pxまでは縮んでいい。でもそれ以下にはするな。余裕があれば 1fr まで伸ばせ」という意味になります。
文章より、コードで見たほうが早いです。
.layout {
display: grid;
/* サイドバーは最小14rem〜最大18rem、本文は残り全部 */
grid-template-columns: minmax(14rem, 18rem) 1fr;
gap: 1.5rem;
}
この 1fr が「残り全部もらう」担当です。サイドバーが18remで止まったら、余ったぶんは本文に流れます。固定px幅で組むと画面幅ごとに破綻しますが、fr と minmax を混ぜると「広い画面では広く、狭い画面では潰れない」が1行で表現できます。
ここで grid-template-rows を足せば、行方向(縦)の高さ配分も同じ発想で書けます。grid-template-rows: auto 1fr auto は「ヘッダーは中身ぶん、本文は残り全部、フッターは中身ぶん」。画面の縦も横も、同じ語彙で組めるのがGridの気持ちよさです。
レスポンシブの主役:auto-fit と minmax
ここが今日のいちばんのヤマです。冒頭で僕がメディアクエリを3つ書いた失敗、その正解がこれです。
.card-grid {
display: grid;
/* カードは最小280px。入るだけ列を作り、余りは均等に分配 */
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
gap: clamp(1rem, 2vw, 1.5rem);
}
読み解きます。repeat(auto-fit, ...) は「列の数はブラウザに数えさせる」という指定です。minmax(min(100%, 280px), 1fr) で「1枚あたり最低280px、入るだけ詰めて、余白は均等配分」。これだけで、ワイドモニタでは4列、タブレットでは2列、スマホでは1列に勝手になります。メディアクエリは1行もいりません。
min(100%, 280px) が地味に効きます。これがないと、画面が280pxより狭いとき(古い小型スマホなど)にカードが画面からはみ出します。min(100%, 280px) は「280px、ただし画面幅は超えるな」という保険です。
auto-fit と auto-fill の違い
ここでよく混乱するのが、auto-fit とそっくりな auto-fill です。1枚だけ図解しておきます。
| 指定 | 空きスペースの扱い | 向いている場面 |
|---|---|---|
auto-fit | 空の列を畳んで、既存カードを伸ばす | 記事一覧、価格表(少数でも横幅を使いたい) |
auto-fill | 空の列をそのまま残す(カードは伸びない) | カレンダー、座席表(空き枠に意味がある) |
カードが2枚しかないとき、auto-fit は2枚を画面いっぱいに広げます。auto-fill は「本当は4枚入る幅だよ」と空席を2つ残します。迷ったら auto-fit で大丈夫です。空きマス自体が情報になるUI(予約表など)だけ auto-fill を選びます。
はみ出しを止める一行:min-width: 0
僕がGridでいちばん時間を溶かしたのが、この落とし穴でした。原因がわからず1時間。知ってしまえば一行です。
Gridアイテム(とFlexアイテム)は、初期状態で中身の最小サイズより小さくなりません。だから、長いURL、横長の表、コードブロックが中に入ると、列がそれに引っ張られて横へ膨らみます。結果、レイアウト全体に横スクロールが出ます。
.content {
grid-area: content;
/* これがないと、長い単語やコードブロックで横スクロールが出る */
min-width: 0;
}
/* 表やコードログを置く領域は、はみ出しを内側で吸収させる */
.content pre {
overflow-x: auto;
}
min-width: 0 は「この領域は中身を無視して縮んでいい」とブラウザに許可を出す指定です。記事ページ、SaaSの管理画面、ドキュメントサイトなど、長文や表を扱う画面ではほぼ必ず出てきます。「Gridなのに横スクロールが消えない」と思ったら、まずこれを疑ってください。
grid-template-areas で骨格に名前を付ける
カード一覧は auto-fit で片付きますが、ページ全体の骨格(ヘッダー・サイド・本文・フッター)は別の道具が向いています。grid-template-areas です。
これは配置を文字列で絵に描く書き方です。文字どおり、CSSの中にレイアウトの見取り図が現れます。下のコードはそのままコピーして、HTMLと一緒に開けば動きます。
<div class="page">
<header class="page-header">ヘッダー</header>
<aside class="page-side">サイドバー</aside>
<main class="page-main">本文がここに入ります</main>
<footer class="page-foot">フッター</footer>
</div>
.page {
display: grid;
grid-template-columns: minmax(14rem, 18rem) 1fr;
grid-template-rows: auto 1fr auto;
/* 文字列がそのまま画面の見取り図になる */
grid-template-areas:
"header header"
"side main"
"foot foot";
gap: 1rem;
min-height: 100svh;
padding: 1rem;
}
.page-header { grid-area: header; }
.page-side { grid-area: side; }
.page-main { grid-area: main; min-width: 0; }
.page-foot { grid-area: foot; }
/* スマホでは1列に積み替える。順序の入れ替えがCSSだけで済む */
@media (width < 768px) {
.page {
grid-template-columns: 1fr;
grid-template-areas:
"header"
"main"
"side"
"foot";
}
}
"header header" のように同じ名前を横に2つ並べると、その領域が2列ぶんにまたがります。スマホ用のメディアクエリでは、文字列を縦に積み替えるだけで「サイドバーを本文の下に回す」が完了します。HTMLの順番を触る必要はありません。見出しの読み上げ順(アクセシビリティ)を保ったまま、見た目だけ入れ替えられるのが大きな利点です。詳しくはアクセシビリティも合わせて見てください。
注意は1つだけ。CSSの領域名と、子要素の grid-area の名前は完全一致させること。"side main" と書いたのに子要素が grid-area: sidebar; だと、その要素は配置されず勝手な場所に飛びます。名前を変えるときは、CSSとHTMLを必ずセットで直してください。
効く場面を3つ
机上の話だけだと使いどころが見えないので、僕が実際に使った3パターンを挙げます。
1. メディアの記事カード一覧。 記事数は3件のときも11件のときもあります。auto-fit なら件数が半端でも余白が自然に伸び縮みします。カード自身を grid-template-rows: auto 1fr auto にしておくと、タグや読了時間で本文の長さがバラついても、ボタンの位置だけはきれいに揃います。
2. SaaS・社内ツールのダッシュボード。 サイド、フィルタ、メイン表、詳細パネルを grid-template-areas で名前付けすると、スマホ表示で順序を変えるのがCSSだけで終わります。ただし表やログを置く領域には min-width: 0 と overflow-x: auto を忘れずに。これを抜くと、データが増えた瞬間に横スクロールで崩れます。
3. 価格表・比較表。 価格カードはボタンが収益に直結します。装飾より「全カードが同じ高さで並ぶ」「ボタンが必ず見える」「スマホで指で押せる」を優先します。色や余白や角丸はデザインシステム側でトークン化しておき、Gridは列の増減だけを担当させると保守が楽です。
僕がやらかした失敗3つ
正直に書きます。Gridは強力なぶん、雑に使うと派手に壊れます。
ひとつ目は冒頭の repeat(3, 1fr) ハードコード。「3列」と数を固定した瞬間に、レスポンシブは死にます。今は列数を書かず、auto-fit + minmax にブラウザへ数えさせるのを基本形にしています。
ふたつ目は min-width: 0 を知らずに、はみ出しをメディアクエリで力技で抑えようとしたこと。幅を狭くするたびに新しいはみ出しが出て、いたちごっこでした。原因はGridアイテムの最小幅。一行で済む話に半日使いました。
みっつ目は、PC幅のスクリーンショットだけ見て「完成」と判断したこと。広告枠と長い日本語の見出しを後から入れたら、余白の計算が崩れて本文が読みづらくなりました。それ以来、確認は必ず360px・768px・1024px・1440pxの4枚でやります。画像の最適化や読み込み速度が絡むギャラリーならパフォーマンス最適化も併読を。
よくある質問
Q. GridとFlexbox、結局どっちを使えばいい? A. 折り返す・縦横を同時に揃える=Grid、一方向に並べるだけ=Flexbox。ページ骨格とカード一覧はGrid、ボタン内やナビの横並びはFlexbox。1つのページで両方を入れ子にして使うのが普通です。
Q. メディアクエリはもう書かなくていい?
A. カード一覧は auto-fit + minmax でほぼ不要になります。ただし「スマホだけサイドバーを下に回す」のような骨格の組み替えは、grid-template-areas をメディアクエリで切り替えるのが素直です。ゼロにはならず、激減します。
Q. 1fr と auto と % はどう違う?
A. auto は中身のぶんだけ、% は親に対する固定割合、1fr は「余ったスペースの分け前」。% は gap と一緒だと計算が合わず溢れがちなので、列幅は fr と minmax を主役にするのが安全です。
Q. Gridなのに横スクロールが消えません。
A. ほぼ min-width: 0 不足です。長いURL・表・コードブロックを含む列に付けてください。それでも残るなら、その中身に overflow-x: auto を当てて、はみ出しを内側で吸収させます。
Q. IE対応は必要?
A. 2026年時点でIEはサポート終了済みです。モダンブラウザはGridを全面的に解釈するので、いまから新規でIEを気にする必要はありません。古い構文(-ms-grid)も不要です。
実際に試した結果
このサイトの記事一覧と関連リンクを、全部 repeat(auto-fit, minmax(min(100%, 280px), 1fr)) の一行に置き換えてみました。それまで散らばっていたメディアクエリが消え、CSSが目に見えて短くなりました。カードを1枚増やしても2枚減らしても、レイアウトを触らなくていいのが効きます。
決定的だったのは min-width: 0 です。長い英語URLとコードブロックで出ていた横スクロールが、本文領域にこの一行を足しただけで止まりました。逆に、PC幅だけ見て満足したレイアウトは、長い日本語見出しや広告枠を入れた途端に崩れます。だから僕は、作り終わりに必ず360pxまで画面を絞って一度壊しにいきます。「広げて満足」ではなく「狭めて確かめる」。CSS Gridを実務で安定させる近道は、結局これに尽きました。
自分のサイトや社内画面で同じ設計を使うなら、教材・テンプレート一覧で CLAUDE.md やレビュー用プロンプトを先に整えると手戻りが減ります。チームでレイアウト設計やレビュー運用ごと固めたいときは、研修・導入相談で実リポジトリ前提の相談ができます。公式の挙動はMDNのCSS Grid Layoutが最も正確です。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
Claude Codeに1ファイルだけ直させる指示文のつくり方
「もっと良くして」で40行も変えられた失敗から学んだ、触る範囲・検証・戻し方をセットにしたClaude Code用の依頼文テンプレートを紹介します。
Claude Code の権限拒否から復旧する: 止まった理由を次の安全手順に変える
Claude Code のコマンドが拒否されたとき、焦って許可を広げずに、拒否理由、代替手順、証拠コマンド、再試行条件へ分解する方法。
Claude Codeにビルド→スモークテスト→自動修正を回させる足場の作り方
最小スモークテストの選び方、失敗ログを食わせて直させるループ、回数上限と確認ゲートで暴走を止める方法を、コピペで動くコード付きで紹介します。