レイアウトの実装
Flexbox は中身が並びを決め、Grid は先に枠を決めて中身を置きます。列を揃えたいなら、答えは Grid です。
図の中身を文字で読む
- Flexbox は中身が並びを決める。Grid は先に枠を決めて中身を置く。
- Flexbox = 一方向 Grid = 行と列
- 幅は中身の量で決まる。数が変わっても崩れない。 中身と関係なく、列の幅が揃う。
- 向きは row か column のどちらか一方。 要素をまたいで配置できる(2列分など)。
- ヘッダーの並び、ボタンの列、タグの群れ。 ページ全体の骨格、カードの一覧、表組み。
- 迷ったときの分かれ目
- 列の幅を揃えたい Grid Flexbox で揃えるには全項目に同じ flex-basis が要る
- 中身の量で幅を変えたい Flexbox Grid の列は先に決まるので、中身に追従しない
- 親の幅で列数を変えたい Grid repeat(auto-fit, minmax(16rem, 1fr)) だけで折り返す
- 親要素の列に子も揃えたい サブグリッド カードの見出し・本文・ボタンの高さが列をまたいで揃う
ひと目で#
Flexbox は中身が並びを決める。Grid は先に枠を決めて中身を置く。
- Flexbox
- 一方向に並べる。幅は中身の量で決まる
- CSS Grid
- 行と列を先に定義する。中身と関係なく揃う
- サブグリッド
- 子要素が、親の列と行をそのまま使う
- clamp
- 最小、推奨、最大を1つの関数で書く
- メディアクエリ
- 画面の幅で分岐する
- コンテナクエリ
- 親要素の幅で分岐する。部品が場所を選ばなくなる
使い分け#
| こういうとき | 使うもの |
|---|---|
| 列の幅を揃えたい | Grid |
| 中身の量で幅を変えたい | Flexbox |
| 親の幅に応じて列の数を変えたい | Grid の auto-fit と minmax() |
| カードの中の見出し、本文、ボタンを、列をまたいで揃えたい | サブグリッド |
| 置く場所によって部品の形を変えたい | コンテナクエリ |
| 画面全体の骨格を変えたい | メディアクエリ |
| 文字や余白を幅に連動させたい | clamp() |
画面の幅か、親の幅か#
同じカード部品を、本文の広い列と脇の狭い列の両方に置くとします。メディアクエリは画面の幅しか見ないので、脇に置いても「画面は広い」と判断して横組みのまま崩れます。
図の中身を文字で読む
- 同じカード部品を、本文の広い列と脇の狭い列に置いたとき
- ✕ メディアクエリ(画面の幅を見る) ○ コンテナクエリ(親の幅を見る)
- 画面 1280px 画面 1280px
- 本文の列 脇の 親が広い
- 横組みで収まる 列 横組み 縦積み
- 脇に置いても横組みのまま。部品は「画面は広い」と判断する 親が狭ければ縦積みに落ちる。部品が場所を選ばなくなる
- 列からはみ出し、写真も文字も潰れる 同じ部品を、本文にも脇にも記事下にも置ける
- 親側に container-type: inline-size; を書き、部品側で @container (min-width: 30rem) を見る。
- 画面全体の骨格を切り替えるときはメディアクエリ。置き場所が変わる部品はコンテナクエリ。
コンテナクエリなら、部品は自分が入っている箱の幅を見ます。部品側から置き場所を知らなくてよくなるので、同じ部品をどこにでも置けます。
このコードで動いている見本です
画面全体の骨格(1カラムか2カラムか)を切り替えるときはメディアクエリ。置き場所が変わりうる部品はコンテナクエリ、と分けます。
間違えやすいところ#
- 何でも Flexbox で組む列を揃えるために、全項目へ同じ
flex-basisを書くことになる - Grid の列数をブレークポイントごとに手で書く
auto-fitとminmax()で足りることが多い gapの代わりにmarginを使う端の余白を打ち消す記述が要る- 部品の中でメディアクエリを使う別の場所に置いた瞬間に壊れる。コンテナクエリを使う
clamp()の推奨値をvwだけで書く文字サイズの設定に追従しなくなる。remを足す- ブレークポイントを端末の名前で決める端末は増える。中身が崩れる幅で決める
orderやrow-reverseで見た目の順を変えて放置するタブの順序と食い違う
直す例#
架空の案件を1つ例にとります。どこを見て、何をどう直すかを書きます。
歯科医院サイトの「診療時間」カードです。本文の下では横組み、狭いサイドバーでは縦積みにしたい。部品の中に @media (max-width: 48rem) を書いて対応していますが、サイドバーは画面が広いままなので、狭い列に横組みが押し込まれて表が潰れます。
部品が見ているのが画面の幅だからです。親の .side に container-type: inline-size を付け、部品側の分岐を @container (min-width: 30rem) に置き換えると、同じカードが置き場所に合わせて形を変えます。
同じページで、料金の一覧が Flexbox のまま flex-basis: 30% で3列に揃えてありました。項目が4つになった日に崩れます。ここは列を先に決める側なので、Grid の auto-fit と minmax() に替えます。ブレークポイントは1つも書きません。
確認する#
- 部品の中のメディアクエリが、コンテナクエリに置き換わっている
- 列を揃えたい一覧が Grid で組まれている(
flex-basisの割合指定が残っていない) - Grid の列に
minmax(0, 1fr)が使われている(1frだけだと長い語で横にあふれる)
使えるパターン#
この話が要る場面#
このページが答えになる、現場の困り事です。困り事の側から探すと、同じ場所に着きます。
Gridのサブグリッドで、見出し・本文・ボタンの行を列をまたいで揃える。
このコードで動いている見本です
並びの中の間隔は gap。端の余白を打ち消す記述が要らなくなる。
このコードで動いている見本です
列の幅を揃えたいならGrid、中身の量で幅を変えたいならFlexbox。
このコードで動いている見本です
repeat(auto-fit, minmax(16rem, 1fr)) だけで折り返る。ブレークポイントを書かなくてよい。
このコードで動いている見本です
メディアクエリではなくコンテナクエリを使う。画面幅ではなく親の幅で分岐する。
このコードで動いている見本です
clamp() を使う。推奨値に rem を含めないと、利用者の文字サイズ設定に追従しなくなる。
このコードで動いている見本です
この場面のほかの困り事と、そのまま貼れる部品は並べる・揃える、余白と幅を決める、CSSで詰まるにまとめてある。逆引きには全188件。
場面で選ぶ#
この値は作業台で決められます。和文の予約ページに載せて、幅と明暗を替えて確かめてから CSS を持ち帰る: 並べ方(Flexbox と Grid)
組みたい場面を選ぶと、Flexbox と Grid のどちらで組むかの判断が1文で出ます。完成 CSS は実物のプレビュー付きです。
Flexbox項目の幅は文字数で決まり、列を揃える必要が無いので Flexbox で組みます。
/* ナビの横並び */
.nav { display: flex; flex-wrap: wrap; align-items: center;
gap: var(--space-2) var(--space-5); }
.nav a { padding: var(--space-2) var(--space-3); }
この道具は JavaScript で動きます。無効の場合は、上の使い分けの表と下の既定値のコードを使ってください。