CSS講座 / Flexboxの整列・折り返し・gap・伸縮

Flexboxの整列は、主軸にjustify-content、交差軸のアイテムにalign-itemsを使います。複数行のまとまりを動かすalign-contentは、折り返せるコンテナーに交差軸の空きがあることも必要です。

整列の対象と空きを確認する

指定 対象 確認する条件
justify-content 主軸方向の配置 アイテムのサイズ決定後に残る空き
align-items / align-self 各アイテムの交差軸方向 行の高さとアイテム自身のサイズ
align-content 行のまとまり wrap可能なコンテナーと交差軸の空き
gap 隣のアイテム・行の間隔 整列で加わる空きとは別

rowとcolumnでは軸の物理方向が変わります。columnのjustify-contentを比較するなら、内容より大きい高さを用意しないと移動する空きがありません。space-betweenとgapを併用すると、実際の間隔はgapより大きくなり得ます。

growや主軸のautoマージンが空きを使い切ると、justify-contentを変えても差が見えない場合があります。align-itemsは各行の中、align-contentは行全体を扱います。nowrapの単一行コンテナーではalign-contentは効きません。MDNのFlexbox整列と二つの軸を参照してください。

アイテムと行の整列を比較する

次をflex-align.htmlとして保存します。最初は高さの違う三項目、次は二行に折り返す四項目です。後者には高さ280pxを用意し、行を動かす空きを作っています。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>flex-align.html</title>
  <style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.toolbar { display: flex; gap: 12px; justify-content: space-between; align-items: center;
 width: 100%; max-width: 640px; min-height: 120px; border: 2px solid #789; padding: 12px; }
 .toolbar > div { padding: 8px; background: #dceefa; }
 .lines { display: flex; flex-wrap: wrap; gap: 12px; width: min(100%, 360px); height: 280px;
 align-items: center; align-content: flex-start; border: 2px solid #789; padding: 12px; }
 .lines > div { flex: 0 0 140px; padding: 8px; background: #e8f5de; }
 .tall { min-height: 60px; }
  </style>
</head>
<body>
<h1>Alignment on one line and multiple lines</h1>
<h2>Toolbar</h2><div class="toolbar"><div>Short item</div><div>Two-line item<br>Second line</div><div>Button position</div></div>
<h2>Line group</h2><div class="lines"><div>1</div><div class="tall">2<br>Second line</div><div>3</div><div class="tall">4<br>Second line</div></div>
</body>
</html>
  1. .toolbarのjustify-contentだけをcenter、flex-startへ変え、三項目のまとまりを比べてspace-betweenへ戻します。gapとサイズはそのままにします。
  2. align-itemsをflex-startへ変えて上端を確認します。stretchでは交差軸サイズがautoの項目が伸びます。centerへ戻します。
  3. .linesのalign-contentをcenterへ変え、二行全体が動くことを見ます。flex-startへ戻し、今度はalign-itemsだけをflex-startへ変えて各行内の差を見ます。
  4. flex-wrapだけをnowrapへ変えます。この例は縮小を無効にしているため横にはみ出し、複数行の整列もできなくなります。wrapへ戻します。

grow・shrink・basisを分ける

flex: 1 1 192pxは、拡大率、縮小率、基準サイズの順です。basisは配分の開始点で、最終幅を保証する値ではありません。学習では省略形による初期値の違いを避けるため、三つとも記述して比べます。

幅640pxに基準100pxの三項目と20pxのgapが二つなら、残りは640−300−40=300pxです。growが1:2:1なら75・150・75pxを加え、最終幅は175・250・175pxになります。これは最小サイズやmin/max制約が関係しない条件の計算です。

shrinkは数値だけでなく基準サイズでも重み付けされます。基準200pxと100px、shrinkが両方1で60px縮める単純な条件なら、40pxと20pxを引きます。実際にはpadding、border、最小サイズなども調べます。0 1 192pxは拡大せず縮小し、0 0 192pxはどちらにも参加しません。MDNのflexで短縮形の規則を確認できます。

幅を配分してカードを折り返す

次をflex-size.htmlとして保存します。.calcには余分な枠やpaddingを付けず、最大幅640pxで先の計算と対応させています。カードは192pxを基準に行を決め、その行の残りを配分します。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>flex-size.html</title>
  <style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.calc { display: flex; gap: 20px; width: min(100%, 640px); background: #eef2f6; }
 .calc > div { flex: 1 1 100px; min-width: 0; height: 80px; background: #dceefa; }
 .calc > div:nth-child(2) { flex-grow: 2; }
 .cards { display: flex; flex-wrap: wrap; gap: 16px; list-style: none; padding: 0; }
 .cards > li { flex: 1 1 192px; min-width: 0; padding: 16px; background: #e8f5de; overflow-wrap: anywhere; }
  </style>
</head>
<body>
<h1>Flex values ​​and space distribution</h1><div class="calc"><div>grow 1</div><div>grow 2</div><div>grow 1</div></div>
<h2>Wrapping card</h2><ul class="cards" role="list"><li>First card</li><li>Second card</li><li>Third card</li><li>Fourth card</li></ul>
</body>
</html>

広い画面で.calcが640pxであることを確認し、子の幅を開発者ツールで測ります。二番目のgrowだけを1へ変えると、三つとも200pxになる条件です。比べたら2へ戻します。

.cards > liのgrowだけを0にすると、最終行が余りを埋めなくなります。1へ戻して画面を狭めると、行ごとに項目数と配分が変わります。各行の幅は独立して決まるため、最終行の列幅が上の行と一致するとは限りません。

nowrapとflex: 0 0 192pxへ変えると、折り返さず縮小もしないはみ出しを比較できます。確認後はwrapと1 1 192pxへ戻します。例のulにあるrole="list"は、nav外でlist-style: noneを使うとSafariのアクセシビリティツリーでリストとして扱われない場合への対応です。段落とリストも参照してください。

最小幅と長い文字列を別々に扱う

Flexアイテムの自動最小幅が縮小を妨げる場合はmin-width: 0を検討します。ただし、箱を縮めても文字が自動的に折り返すわけではありません。文字列にはoverflow-wrap: anywhereなどのルールが別に必要です。

次をflex-long-text.htmlとして保存します。長い文字列は実在の住所ではなく、空白のない識別子の例です。画面を狭めても、案内の横の文字を複数行で読めるか確認します。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>flex-long-text.html</title>
  <style>
* { box-sizing: border-box; }
body { margin: 16px; font: 16px/1.6 sans-serif; color: #243447; }
h1 { font-size: 24px; } h2 { font-size: 20px; margin-top: 32px; }
a { color: #145f8f; } a:focus-visible { outline: 3px solid #b4470b; outline-offset: 3px; }
.notice { display: flex; gap: 12px; max-width: 600px; border: 2px solid #789; padding: 12px; }
 .icon { flex: 0 0 48px; background: #ffe4b5; }
 .text { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; background: #dceefa; }
 .text p { margin: 0; }
  </style>
</head>
<body>
<h1>Long strings and minimum sizes</h1><div class="notice"><div class="icon">Notice</div><div class="text"><p>ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</p></div></div>
</body>
</html>

.textのoverflow-wrapだけをnormalへ変え、箱の外へ文字が出ることを確認してanywhereへ戻します。最小幅の影響を分けて見るならnormalの状態でmin-width: 0も外し、配置への影響を見て両方を復元します。MDNのoverflow-wrap、はみ出しと改行を参照してください。問題を隠す前に、軸と空き、箱の最小幅、文字の折り返しの順に調べます。