CSSレイアウトアルゴリズム(Flexbox/Gridの解決過程)
Flex/Gridが「なぜその幅になるか」を式で説明できるようになり、潰れ・はみ出しを当て推量せず直せる。包含ブロック・BFC・内在サイズの解決順序を原理から解き明かします。
- 1.レイアウトは包含ブロック(containing block)を基準に、まず利用可能幅を確定し、その中で各ボックスの幅・高さを内在サイズ(min-content/max-content)と指定値から解く2段構えで進む。
- 2.Flexは主軸に沿った1次元の再分配で、flex-basis を起点に伸長(grow)と収縮(shrink、basis×factor 重み)でフリースペースを配り、min-content がハードな下限になる。
- 3.Gridはトラックサイジングを min/max の2成分で解き、intrinsic→flexible(fr)の順に確定。サブグリッドは親トラックを継承するため、親の解決が完了して初めて子が解ける。
レイアウトは「基準確定→サイズ解決」の2段構え
CSSのレイアウトは魔法ではなく、決まった順序を持つアルゴリズムです。どんなレイアウトモード(通常フロー・Flex・Grid)でも、共通する骨格は2段階です。第1段で各ボックスの包含ブロック(containing block)、すなわち寸法とパーセンテージ解決の基準になる矩形を確定し、第2段でその中で自分のサイズを解きます。「width: 50% が何の50%か」「height: 100% がなぜ効かないことがあるか」は、すべてこの包含ブロックの決まり方で説明できます。
包含ブロックは要素の position で決まります。static/relative の通常フローなら直近のブロックレベル祖先のコンテンツ領域、absolute なら直近の position が static でない祖先のパディング領域、fixed ならビューポート、そして transform や filter、will-change などを持つ祖先がいればそれが fixed の基準すら奪います。横方向のパーセンテージは包含ブロックの幅を、縦方向のパーセンテージも原則として幅ではなく高さを基準にしますが、包含ブロックの高さが自動(auto)だと縦パーセンテージは解けず無視される——これが「親に高さ指定がないと height: 100% が効かない」の正体です。
padding と margin は縦横ともに包含ブロックの幅を基準にします。padding-top: 10% が親の高さでなく幅基準なのはこのためで、正方形を作る「アスペクト比ハック」が成立する根拠です(今は aspect-ratio で代替可能)。一方 width: 50% は幅基準、height: 50% は高さ基準と方向ごとに基準が分かれる点に注意してください。
BFC:レイアウトの独立した計算領域
通常フローで効いてくるのがBFC(Block Formatting Context、ブロック整形コンテキスト)です。BFCは内部のブロックボックスが配置される独立した区画で、外と相互干渉しません。これが生成する効果は3つに集約できます。
| 効果 | 内容 | 実務での用途 |
|---|---|---|
| マージン相殺の遮断 | BFC内外で隣接マージンが結合しない | 意図しない上下マージンの吸収を止める |
| フロートの内包 | 内部のfloatをBFCが高さに算入する | 親が float を囲めず高さ0になるのを防ぐ |
| フロートとの重なり回避 | BFCはfloatの下に潜り込まない | サイドバー横の段組みレイアウト |
BFCは overflow が visible 以外、display: flow-root、float、absolute/fixed、Flex/Gridアイテム、contain: layout などで生成されます。display: flow-root は副作用なしにBFCだけを作る目的で導入された値で、overflow: hidden のBFC化を副作用目的で使う旧来のハックを置き換えます。Flexコンテナ・Gridコンテナのアイテムは自動的にBFC(正確には独立した整形コンテキスト)になるため、Flex/Grid内ではマージン相殺が起こらない点も、この枠組みで一貫して理解できます。
内在サイズ:min-content と max-content
Flex/Gridのサイズ解決を理解する鍵は、**内在サイズ(intrinsic size)**という2つの基準量です。これらはコンテンツ自体が要求する寸法で、外から幅を与えなくても計算できます。
- max-content:折り返しを一切許さないときの幅。テキストなら全文を1行に並べた幅で、コンテンツが「理想とする」最大の幅です。
- min-content:これ以上縮めると壊れる最小幅。テキストなら**最長の単語(分割不能な最小単位)**の幅で、これがハードな下限になります。
コンテンツ: "responsive layout"
max-content 幅 = |responsive layout| ← 1行に収めた幅(折り返しなし)
min-content 幅 = |responsive| ← 最長の分割不能単語の幅
width: min-content / max-content / fit-content() はこの値を直接指定する手段で、Flex/Gridの自動サイズ決定でも内部的に使われます。重要なのは、min-content がレイアウトの絶対的な床になることです。後述するFlexの収縮もGridの縮小も、min-content より小さくはできません。だからこそ、長いURLやコードを含む要素が min-width: 0 なしに親をはみ出させる事故が起きます——Flexアイテムの min-width 初期値は 0 ではなく auto(=内容の min-content)だからです。
Flexアイテムは既定で min-width: auto を持ち、これはコンテンツの min-content サイズに解決されます。そのため flex-shrink を効かせても、長い文字列を含むアイテムは min-content 未満に縮まず、コンテナをはみ出します。min-width: 0(縦軸なら min-height: 0)を明示すると床が外れ、期待どおり収縮します。Flexの「はみ出しが直らない」相談のほとんどはこれが原因です。
Flexbox:主軸に沿った1次元の再分配
Flexレイアウトは主軸(main axis)方向のフリースペースの再分配です。アルゴリズムの骨子は次の順序で進みます。
1. 各アイテムの flex-basis を決める(auto なら width/内在サイズに解決)
2. basis の合計とコンテナ主軸サイズを比較し、フリースペースを算出
free = コンテナ主軸サイズ − Σ(flex-basis + margin)
3. free > 0 なら grow で配分、free < 0 なら shrink で吸収
4. 各アイテムの min/max とフレックス下限(min-content)でクランプ
5. 交差軸(cross axis)で align を解決し、最終位置を確定
伸長は単純で、free を flex-grow の比で配分します。難しいのは収縮で、flex-shrink はflex-basis で重み付けした比率で不足分を吸収します。つまり「basis が大きいアイテムほど多く縮む」のが既定の挙動です。
収縮係数の重み = flex-basis × flex-shrink
アイテム i の縮小量 = |free| × (basis_i × shrink_i) / Σ(basis_j × shrink_j)
この basis 重み付けは、見た目の比率が flex-shrink の値そのままにならない理由です。さらに、あるアイテムが min-content(または min-width)に達してそれ以上縮めなくなると、そのアイテムはフリーズされ、残りの不足分が他アイテムへ再配分されます。この「凍結→再計算」を全アイテムがクランプ範囲内に収まるまで反復するため、Flexのサイズ解決は単純な一次配分ではなく反復アルゴリズムです。
flex: 1 は 1 1 0%(basis 0 から grow)、flex: auto は 1 1 auto(basis を内在サイズから取って grow)です。前者は内容量に関係なくフリースペースを等分するため列幅が揃い、後者は内容の多いアイテムほど広くなる。等幅の段組みなら flex: 1、内容に応じた自然な配分なら flex: auto を選びます。
Grid:トラックを min/max の2成分で解く
Gridは2次元ですが、行と列のトラックサイジングは各軸で独立に解けます。各トラックは内部的に最小サイズ関数と最大サイズ関数の2成分を持ち(minmax(min, max) がその素の形)、解決は決まった段を踏みます。
1. 各トラックの base size を最小関数で初期化(auto/min-content なら下限から)
2. intrinsic なトラック(auto, min/max-content, fit-content)を
アイテムのコントリビューションで拡張(base size を成長)
3. フリースペースを fr トラックへ比率配分(flexible sizing)
4. 残余があれば auto トラックへ分配(stretch)
ポイントは順序です。fr(flexible length)の解決は、auto や min-content などの内在トラックを確定させた後に行われます。fr は「残りスペースの比率配分」なので、固定幅・内在幅が先に取り分を確保し、その残りを fr が分け合います。1fr が思ったより小さいのは、内在トラックが先にスペースを食っているケースが大半です。また Gridアイテムにも min-width: auto(=min-content 下限)が効くため、Flexと同じく minmax(0, 1fr) と書かないと長いコンテンツでトラックがはみ出します——1fr の実体は minmax(auto, 1fr) だからです。
| トラック指定 | 最小関数 | 最大関数 | 挙動 |
|---|---|---|---|
| 1fr | auto(=min-content床) | flex(1) | 残余を比率配分。ただし内在下限あり |
| minmax(0, 1fr) | 0 | flex(1) | 下限0で純粋な比率配分。はみ出しを防ぐ |
| auto | min-content | max-content | 内容に応じて伸縮 |
| fit-content(200px) | min-content | min(max-content, 200px) | max-content で頭打ち |
サブグリッド:親の解決完了が前提
subgrid は子グリッドが親のトラック定義を継承する仕組みで、ネストした要素の罫線を親グリッドにぴたりと揃えられます。アルゴリズム上の本質は計算順序の依存にあります。サブグリッドのトラックは自前で fr 配分や内在サイズ解決を行わず、対応する親トラックのサイズをそのまま借りるため、親の軸が完全に解決されるまで子は確定できません。
通常のネストグリッド: 親と子は独立。子は自分の利用可能幅で自前に解く
サブグリッド: 子トラック = 親の対応トラックの解決結果を継承
→ 親軸の確定 → 子の配置、という直列依存になる
ただし内在サイズのコントリビューションは双方向です。サブグリッドのアイテムが持つ min-content/max-content は、親トラックのサイジングに反映されます。つまり「親が子の内在サイズを集めてトラックを確定し、確定したトラックを子へ配る」という、集約と分配の往復を含む解決になります。grid-template-columns: subgrid を片軸だけ、もう片軸は独自定義、という混在も可能で、その場合は subgrid 軸だけが親依存、もう一方は通常どおり自前で解きます。
レイアウト問では、(1) 包含ブロックの決まり方(position 別の基準と、auto 高さで縦%が無視される件)、(2) BFCの3効果(マージン相殺遮断・float内包・float回避)と生成条件、(3) min-content がハード下限で Flex/Grid の min-width: auto がはみ出しを生む件、(4) Flex収縮の basis 重み付けと凍結反復、(5) Grid の内在→fr の解決順、(6) サブグリッドの親依存、が頻出です。「1fr の実体は minmax(auto, 1fr)」は定番の引っかけです。
まとめ
CSSレイアウトは包含ブロックで基準を確定→その中でサイズを解く2段アルゴリズムです。基準は position で決まり、padding/margin の % は常に幅基準、height: % は包含ブロック高さが auto だと無視されます。BFCはマージン相殺の遮断・float内包・float回避を独立区画で実現します。Flex/Gridのサイズ解決は内在サイズ(min-content/max-content)を土台にし、min-content がハードな床になるため min-width: auto がはみ出しの主因です。Flexは flex-basis 起点に grow/shrink(shrink は basis 重み付け)で再分配し、クランプを凍結反復で解きます。Gridは min/max の2成分で内在→fr の順にトラックを確定し、サブグリッドは親トラックを継承するため親軸の解決完了が前提になります。土台の単位やボックスモデルは CSS、可変幅設計への応用は レスポンシブデザイン で、レイアウト再計算のコストは リフローとリペイントを引き起こすCSSプロパティの分類 と レンダリングパイプライン詳説 で押さえると、解決過程から性能までが一本につながります。
Web/フロントエンド Article
CSSレイアウトアルゴリズム(Flexbox/Gridの解決過程)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
CSS
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 5
導入後に効く点
Flexは主軸に沿った1次元の再分配で、flex-basis を起点に伸長(grow)と収縮(shrink、basis×factor 重み)でフリースペースを配り、min-content がハードな下限になる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 5
判断チェックリスト
- 自社の用途が「CSS / レイアウト」に近いか確認する。
- 強みである「レイアウトは包含ブロック(containing block)を基準に、まず利用可能幅を確定し、その中で各ボックスの幅・高さを内在サイズ(min-content/max-content)と指定値から解く2段構えで進む。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。