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: auto1 1 auto中身の幅から出発して、余りを分け合う
flex: none0 0 auto中身の幅のまま、伸びも縮みもしない
flex: 1(正の数)1 1 00 から出発するので、比率どおりの幅になる

中身の幅が 50・100・150px のアイテムを幅 600px のコンテナに並べて比べました。

指定Chrome の幅
指定なし50・100・150px
flex: auto150・200・250px
flex: 1200・200・200px
flex: 1・flex: 2・flex: 1150・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: 1200・100px
1 つ目だけ flex-shrink: 2160・140px
2 つ目だけ flex-shrink: 0100・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: 0160 × 20px(1 行)450px
white-space: nowrap160 × 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)600px700px
min-width: 0200px600px
overflow: hidden200px300px

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-start0・40・80px
space-between0・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 欄は検証されず、全角スペースや全角数字の入った値もそのまま出力され、ブラウザはその宣言を捨てます。