Flexbox(フレックスボックス)は、親要素に display: flex を指定するだけで子要素を横並びにでき、余った幅の配分や揃え方まで決められるレイアウト方式です。仕様は CSS Flexible Box Layout Module Level 1(勧告候補草案)です。display: flex は Chrome 29・Edge 12・Firefox 20・Safari 9 以降、Flexbox での gap は Chrome 84・Firefox 63・Safari 14.1・iOS Safari 14.5 以降で使えます(MDN browser-compat-data 8.1.4)。
使い方の解説でつまずきやすいのは、「flex: 1 を付けたのに均等にならない」「日本語のラベルが 1 文字ずつ縦に折り返す」「align-content が効かない」といった場面です。どれも仕様に理由が書かれています。この記事では仕様の該当箇所を示しながら、Chrome 152(macOS、2026 年 10 月 2 日)でテスト用の要素に宣言を当て、getComputedStyle() と getBoundingClientRect() で読み取った値で確かめます。
横並びの基本:主軸と交差軸
display: flex を指定した要素(フレックスコンテナ)の子要素(フレックスアイテム)は、何も指定しなければ横一列に並びます。これは flex-direction の初期値が row だからです。Flexbox には 2 本の軸があります(2 章)。
- 主軸:
flex-directionの向き。rowなら左から右、columnなら上から下。 - 交差軸:主軸と直交する向き。
揃えのプロパティはどちらかの軸に結び付いています。justify-content は主軸、align-items・align-self は交差軸でアイテムを揃え、align-content は折り返した行を交差軸で並べます。横並び(row)なら justify-content が左右、align-items が上下です。flex-direction: column に変えると、この関係が入れ替わります。
軸は書字方向に従います。writing-mode: vertical-rl の縦書きコンテナでは、row は上から下に進みます。
row-reverse や column-reverse は表示位置だけを変えます。幅 600px のコンテナに 100px のアイテムを 3 つ並べて row-reverse にすると、x = 500・400・300px に描かれましたが、Tab キーでのフォーカス移動やスクリーンリーダーの読み上げは HTML の順のままです。仕様の 5.4 節も、並べ替えを正しいソース順の代わりに使わないよう求めています。
中央揃え:justify-content・align-items と margin: auto
上下左右の中央揃えは、コンテナに 2 行書くだけです。
.parent {
display: flex;
justify-content: center; /* 主軸(左右)の中央 */
align-items: center; /* 交差軸(上下)の中央 */
}
400 × 200px のコンテナに 100 × 40px のアイテムを入れると (150, 80) に置かれました。アイテム側に margin: auto を指定しても同じ (150, 80) です。自動マージンは justify-content より先に空きを吸収するからです(8.1 節)。
align-items: center は行の中でそれぞれのアイテムを中央に置きます。高さ 40px と 80px のアイテムを並べると、上端は 80px と 60px になり、中心がそろいます。逆に何も指定しないと、align-items の初期値 normal は stretch として働き、高さを指定していないアイテムはコンテナの高さまで伸びます(120px のコンテナで 120px)。「横並びにしたら高さがそろった」のはこのためです。
等分と伸び縮み:flex: 1・flex: auto・初期値の違い
アイテムには 3 つのプロパティがあります(7 章)。
flex-grow:余った幅(正の空き)をどの比率でもらうか。flex-shrink:足りないとき、はみ出した分をどの比率で削るか。flex-basis:伸び縮みの出発点。autoは「widthがあればその値、なければ中身の幅」。
ショートハンドの flex で 3 つをまとめて指定します。仕様 7.1.1 節の代表的な値は次のとおりです。
| 指定 | 展開後 | 動き |
|---|---|---|
flex: initial(初期値) | 0 1 auto | 中身の幅。縮むが伸びない |
flex: auto | 1 1 auto | 中身の幅から出発して、余りを分け合う |
flex: none | 0 0 auto | 中身の幅のまま、伸びも縮みもしない |
flex: 1(正の数) | 1 1 0 | 0 から出発するので、比率どおりの幅になる |
中身の幅が 50・100・150px のアイテムを幅 600px のコンテナに並べて比べました。
| 指定 | Chrome の幅 |
|---|---|
| 指定なし | 50・100・150px |
flex: auto | 150・200・250px |
flex: 1 | 200・200・200px |
flex: 1・flex: 2・flex: 1 | 150・300・150px |
flex: auto は余りの 300px を 100px ずつ中身の幅に足すので、中身の差が残ります。flex: 1 は出発点が 0 なので 600px 全体が余りになり、均等になります。「3 等分のカラム」に flex: 1 を使うのはこのためで、Chrome の計算値は flex-grow: 1・flex-shrink: 1・flex-basis: 0% でした。
縮むときの配分:flex-shrink
アイテムの合計がコンテナより大きいと、アイテムが縮みます。削られる量は均等ではありません。9.7 節では flex-shrink に各アイテムの基準サイズを掛けた値(スケール済み縮小係数)で配分するので、大きいアイテムほど多く削られます。
flex-basis が 400px と 200px の空のアイテムを幅 300px のコンテナに入れると、300px はみ出します。
| 指定 | Chrome の幅 |
|---|---|
両方 flex-shrink: 1 | 200・100px |
1 つ目だけ flex-shrink: 2 | 160・140px |
2 つ目だけ flex-shrink: 0 | 100・200px |
1 行目は 400 × 1 と 200 × 1、つまり 2 : 1 で 300px を削るので、どちらも半分になります。flex-shrink: 2 だと重みが 800 と 200 になり、1 つ目が 240px、2 つ目が 60px 削られます。アイコンや固定幅のサイドバーを縮ませたくないときは flex-shrink: 0 を付けます。
日本語ラベルが1文字ずつ縦に折り返す:min-width: auto
前の節の例は中身のない箱でした。中身のあるアイテムには、4.5 節の「自動最小サイズ」が効きます。スクロールコンテナでない限り、アイテムは中身に基づく最小サイズ、だいたい「中身の中でいちばん長い改行できない部分」より小さくなりません。min-width の初期値 auto がこれです。
ここで日本語と英語の差が出ます。日本語の文章はほぼどの文字のあいだでも改行できるので、最小サイズは 1 文字分しかありません。幅 300px のコンテナに「お問い合わせはこちら」(16px)というラベルと、flex: none; width: 290px の要素を並べた結果です。
| ラベルの指定 | ラベルの幅と高さ | コンテナのスクロール幅 |
|---|---|---|
| 指定なし | 16 × 200px(1 文字ずつ 10 行) | 306px |
flex-shrink: 0 | 160 × 20px(1 行) | 450px |
white-space: nowrap | 160 × 20px(1 行) | 450px |
| 指定なし、隣の要素を 200px に | 100 × 40px(2 行) | 300px |
ラベルは 1 文字の幅まで縮められ、縦に 10 行並びました。英語の「Contact us here」で同じことをすると、いちばん長い単語「Contact」の幅(約 63px)で止まり、2 行になります。日本語のほうが縮む余地が大きいぶん、崩れ方が極端になります。
直し方は場面によって変わります。ラベルを 1 行に保つなら、ラベル側に flex-shrink: 0(または white-space: nowrap)を付けたうえで、隣の要素が縮めるようにします。隣が固定幅のままだと、上の表のとおりコンテナからはみ出します。
逆に、URL や英単語、型番が入ったアイテムは縮まずにはみ出します。幅 300px の行で、改行できない 600px の子を持つアイテムと 100px の要素を並べると次のようになりました。
| アイテムの指定 | アイテムの幅 | コンテナのスクロール幅 |
|---|---|---|
指定なし(min-width: auto) | 600px | 700px |
min-width: 0 | 200px | 600px |
overflow: hidden | 200px | 300px |
min-width: 0 で割り当て幅まで縮みますが、子はアイテムからはみ出したままです。overflow: hidden を付けるとアイテムがスクロールコンテナになり、自動最小サイズが 0 になって子が切り取られます。テキストなら min-width: 0 に overflow-wrap: anywhere か text-overflow: ellipsis を組み合わせます。
「flex: 1 なのに均等にならない」も同じ理由です。flex: 1 の 3 つのアイテムを 600px に並べ、真ん中に幅 400px の要素を入れると 100・400・100px になりました。真ん中は 400px より小さくなれず、残りを両側が分け合います。
折り返し(flex-wrap)と align-content
flex-wrap の初期値 nowrap では、はみ出してもすべて 1 行に並びます。wrap にすると入りきらないアイテムが次の行に回ります。各行の中でアイテムを揃えるのが align-items、行そのものを揃えるのが align-content です(8.4 節)。
6 章には混乱しやすい区別があります。flex-wrap: wrap のコンテナは、アイテムが 1 行に収まっていても「複数行」のコンテナとして扱われ、nowrap のコンテナは「単一行」です。仕様には、単一行のコンテナでは「align-content は効果がない」とあります。
- 高さ 200px、
flex-wrap: wrap、align-items: flex-start、align-content: centerで、40px のアイテム 3 つが 1 行に収まっている場合、行は y = 80px に下がりました。 - 同じ指定で
nowrapにすると、アイテムは y = 0 のままでした。
align-content の初期値は normal で、Flexbox では stretch として働き、行がコンテナの高さを分け合います。400 × 200px の折り返すコンテナに 150 × 40px のアイテムを 6 つ入れると 3 行になり、各行の上端は次のとおりでした。
align-content | 各行の上端 |
|---|---|
normal(初期値) | 0・66.66・133.34px |
flex-start | 0・40・80px |
space-between | 0・80・160px |
商品カードやタグを折り返して並べたとき「行のあいだに妙な隙間が空く」のは、この normal の伸びが原因です。詰めたいなら align-content: flex-start を指定します。
余白は gap と margin-left: auto で
アイテムのあいだの余白には gap を使います。コンテナの外周には余白が付かないので、margin で間隔を作って最後の要素だけ打ち消す、といった手間がいりません。2 つの値は行の間隔、列の間隔の順です。flex-wrap: wrap・gap: 20px で 100px のアイテム 4 つを幅 340px に並べると、x = 0・120・240px の 3 つのあと、4 つ目は (0, 30px) に折り返しました(アイテムの高さ 10px + 行の gap 20px)。iOS Safari 14.4 以前では Flexbox の gap が効かないので、古い iPhone を対象にするなら注意が必要です。
ヘッダーで「ロゴは左、メニューは右」にしたいときは、右に寄せたい最初の要素に margin-left: auto を付けます。
.header { display: flex; gap: 16px; }
.header .menu-first { margin-left: auto; }
幅 800px に 120px のロゴと 80px のリンク 2 つを並べ、1 つ目のリンクに margin-left: auto を付けると、x = 0・624・720px になりました。リンクどうしのあいだには gap の 16px が残ります。
なお、Flexbox は 1 行ごとに幅を決めるので、行が変わると縦の位置はそろいません。カード一覧やラベル列のあるフォームのように縦横両方をそろえたいときは CSS Grid が向いています。詳しくは CSS Gridの使い方を参照してください。
ジェネレーターで書き出す
CSSフレックスボックスジェネレーターは、コンテナのルールだけを書き出します。flex-direction・flex-wrap・justify-content・align-items・align-content はプルダウン、gap は自由入力、アイテム数(1〜8)はプレビュー用です。すべてのプロパティが出力されるので、不要な行は消して使います。開いたときの出力は次のとおりです。
.container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: normal;
align-items: normal;
align-content: normal;
gap: 1rem;
}
これは CSS の初期値と同じなので、別の値を選ぶまでブラウザの表示は変わりません。Flexbox では align-items: normal と align-content: normal は stretch、justify-content: normal は flex-start として働きます(CSS Box Alignment Level 3)。折り返した行を伸ばさずに上へ詰めたいときは、align-content で flex-start を選びます。
上下左右の中央揃え:
.container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
align-content: normal;
gap: 0;
}
折り返すタグ一覧(行は上に詰め、行の間隔 8px、タグの間隔 12px):
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: normal;
align-items: normal;
align-content: flex-start;
gap: 8px 12px;
}
アイテム側のプロパティ(flex・flex-grow・flex-shrink・flex-basis・order・align-self・min-width: 0)の入力欄はないので、コピーしたあとに書き足します。justify-content の選択肢は normal・flex-start・flex-end・center・space-between・space-around・space-evenly の 7 つ、align-content には space-evenly がありません。gap 欄は検証されず、全角スペースや全角数字の入った値もそのまま出力され、ブラウザはその宣言を捨てます。