調べて分かる道具箱

Flexboxを触って覚える

まず、これを見てください

下の2つは、子に書いた1行だけが違います。上はflex: 1、下はflex-grow: 1。どちらも数は同じ1なのに、幅のそろい方が変わります。

ねこ
にわとり
かたつむり
子ぜんぶにflex: 1と書いたとき。3つとも同じ幅になります。
ねこ
にわとり
かたつむり
子ぜんぶにflex-grow: 1と書いたとき。中身が長い子ほど広くなります。

なぜこうなるのかは、この下の道具で「書いた行」と「開いた形」を並べた表を見ると分かります。 先に答えを言うと、変わっているのはflex-basis(基準の幅)ただ1つです。

見本から始める
ねこ
にわとり
かたつむり
破線は親の箱です。いまの主軸はよこ向きで、左がはじめ、右がおわりです。justify-content(flex-start)は主軸=よこに効き、align-items(stretch)は交差軸=たてに効きます。flex-wrap は nowrap なので、入りきらないときは折り返さずに縮みます。子の文字の大きさをわざと変えてあるのは、align-items: baselineにしたときに、文字の下の線がそろうところを見えるようにするためです。

いまの主軸と交差軸

justify-content が効くのは「よこ」ではなく「主軸」です。flex-directionを変えると、この矢印ごと向きが変わります。

主軸と交差軸の図主軸交差軸はじめおわり
太い矢印が主軸(justify-content が効く向き)、 細い矢印が交差軸(align-items が効く向き)です。 図が入りきらないときは、図の上で横に動かせます(キーボードでも動きます)。

親に書くもの

displayflex にすると、この箱の中だけが flex の並べ方になります
flex-direction主軸の向きを決めます。ここを変えると justify-content の効く向きも変わります
justify-content主軸に沿った並べ方
align-items交差軸に沿ったそろえ方
flex-wrap入りきらないときに折り返すかどうか
gap(子と子のすきま)と、子の数
pxこ

gap は書かないと 0 です(normalと書かれますが、flex では 0 として使われます)。3つでは違いが出ないものがあるので、子は6つまで増やせます。

子に書くもの

flex の書き方を変えると、開いた形が変わります。同じ「1」でも、flex: 1とflex-grow: 1では、下の表のflex-basis の行だけが違うことを確かめてください。

1つめ(ねこ)

flex を1行も書いていないので、3つとも初期値です。伸びず(flex-grow: 0)、足りなければ縮み(flex-shrink: 1)、幅は中身のぶん(flex-basis: auto)になります。

order
2つめ(にわとり)

flex を1行も書いていないので、3つとも初期値です。伸びず(flex-grow: 0)、足りなければ縮み(flex-shrink: 1)、幅は中身のぶん(flex-basis: auto)になります。

order
3つめ(かたつむり)

flex を1行も書いていないので、3つとも初期値です。伸びず(flex-grow: 0)、足りなければ縮み(flex-shrink: 1)、幅は中身のぶん(flex-basis: auto)になります。

order

書いたものと、開いた形

左があなたが書いた行、右がブラウザが実際に使う3つの値です。 書いていないのに決まっている値があることと、 それが省略形のときと、書かなかったときで違うことが、ここで見えます。

子ごとの、書いた行と、開いた3つの値
子書いた行flex-growflex-shrinkflex-basis
1つめねこ(書かない)0初期値のまま1初期値のままauto初期値のまま
2つめにわとり(書かない)0初期値のまま1初期値のままauto初期値のまま
3つめかたつむり(書かない)0初期値のまま1初期値のままauto初期値のまま

表が入りきらないときは、表の上で横に動かせます(キーボードでも動きます)。

コード

そのまま貼れる形です。上の見本は、この文字列をそのまま当てて描いています。

.parent {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: flex-start;
  align-items: stretch;
  gap: 8px;
}

省略しないで書くと、こうなります。 子のflexが3つの行に開いたところを見てください。

.child1 {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
  align-self: auto;
  order: 0;
}

.child2 {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
  align-self: auto;
  order: 0;
}

.child3 {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: auto;
  align-self: auto;
  order: 0;
}

長い行は、この枠の中だけ横に動かせます(キーボードでも動きます)。

よくある質問

flex: 1 と flex-grow: 1 は同じですか?

違います。flex: 1 は省略形なので、書いていない2つも同時に決まり、flex-grow: 1、flex-shrink: 1、flex-basis: 0% になります。flex-grow: 1 だけを書いた場合、flex-basis はそのプロパティの初期値である auto のままです。基準の幅が 0% なら全部が同じ幅になり、auto なら中身の長さから始まるので幅がそろいません。仕様も、省略形を使うことをすすめる理由として「書かなかったぶんを正しく初期化してくれるから」と書いています。

flex: 1 にしたのに幅がそろわないのはなぜですか?

flex ではなく flex-grow だけを書いている可能性が高いです。もう1つよくあるのが、子の中身が最小の幅より小さくならない場合です。フレックスの子は、既定では中身の最小の幅(いちばん長い単語や、大きさの決まっている画像)より小さくなりません。仕様がそう定めていて、変えたいときは min-width や min-height を書きます。長い単語が1つ入っているだけで、その子だけ広いままになります。

justify-content は横方向、align-items は縦方向ですか?

いいえ。justify-content が効くのは主軸、align-items が効くのは交差軸です。flex-direction: row のときは主軸がよこなので結果的にそうなりますが、flex-direction: column にすると主軸がたてに変わり、justify-content もたてに効くようになります。値は何も変えていないのに効く向きだけが変わるので、ここが一番のつまずきどころです。

order を使って順番を入れかえてもいいですか?

見た目だけを変えたいときに限ります。仕様は「order は見た目のための並べ替えにだけ使うこと。意味の順番を order で作ったスタイルシートは仕様違反である」と明記しています。読み上げソフトが読む順番も、タブキーで移動する順番も、order では変わらないからです。見た目と読み上げで順番が食い違うと、画面を見ない人には筋の通らない順番で伝わります。順番そのものに意味があるなら、HTML の並びを直してください。

gap と margin はどう違いますか?

gap は子と子のあいだにだけすきまを作ります。margin を使うと両はしにも余白が付くので、最後の1つだけ margin を消す、という手当てが要りました。gap にはそれが要りません。flex-direction が row のときは column-gap が子と子のあいだ、row-gap が折り返した行と行のあいだになります。書かないときの値は normal で、フレックスでは 0 として使われます。

flex-wrap: wrap にしたのに折り返しません

子が全部入っているうちは1行のままです。wrap は「入りきらないときに次の行へ送る」という指定で、いつも折り返すという意味ではありません。折り返させたいときは、子を増やすか、flex-basis や width で子の幅を大きくしてください。なお、折り返してできた行そのものの並べ方は align-content という別のプロパティが決めます。

display: flex と display: inline-flex はどう違いますか?

中身の並べ方は同じで、違うのは親の箱そのものの置かれ方です。flex にすると親は行いっぱいの箱になり、inline-flex にすると文字と同じように並ぶ、中身の分だけの幅の箱になります。inline-flex では余っている場所がないので、justify-content を変えても子が動かないことがあります。

プロパティを選ぶと、その場で並びが変わって、CSSが出ます。 いちばんつまずく「flex: 1 は3つの値の省略形」と「justify-content が効くのは主軸」を、目で確かめられます。

flex: 1 は、3つの値をまとめて書いた省略形です

flexは3つのプロパティの省略形です。flex: 1と1つだけ書いても、flex-grow・flex-shrink・flex-basis の3つが同時に決まります。 中身はこうです。

  • flex-grow: 1 ── 場所が余ったときに、どれだけ伸びるか
  • flex-shrink: 1 ── 場所が足りないときに、どれだけ縮むか
  • flex-basis: 0% ── 伸び縮みを始める前の、基準の幅

ここで見落とされるのが3つめです。flex-basisを書いた覚えは無いのに、0% という値がちゃんと入っています。 仕様は「省略形で省いたとき、この値は 0 になる」と定めていて、 実際のブラウザで確かめると0%が返ってきます。

基準の幅が 0 ということは、全員が幅ゼロから始めるということです。 そこから余った場所をflex-growの割合で分け合うので、中身が長かろうが短かろうが、同じ幅になります。flex: 1が「幅をそろえるおまじない」のように使われているのは、 じつはこの3つめの効果です。

書かなかったときと、省略形で省いたときは、値が違います

ここがこのページで一番言いたいところです。「書かない」と「省略形の中で省く」は、別のことです。

3つのプロパティを1つずつ見たときの初期値はflex-grow: 0・flex-shrink: 1・flex-basis: auto。 ところが省略形の中で省いたときに埋まる値はflex-shrink: 1・flex-basis: 0%で、3つめが auto ではなく 0% になっています。

これは書き間違いでも、ブラウザのくせでもありません。仕様がわざわざ注記を付けて「この2つは違う」と書いています。理由も添えてあって、「省略形が、よくある使い方に合うようにするため」。 つまり「flex と書く人は、たいてい幅をそろえたい人だ」という判断が、 仕様のほうに入っているわけです。

だからflex-grow: 1だけを書いた子は、基準の幅が auto のままです。 auto は「中身の大きさ」なので、長い言葉が入っている子ほど、最初から広い。 そこへ余りを等しく足すので、差が残ったまま広がります。 ページのいちばん上で見た2つの違いは、これでした。

仕様自身も、1つずつのプロパティより省略形を使うことをすすめています。 理由は「書かなかったぶんを正しく初期化してくれるから」。 言いかえると、1つずつ書くと初期化されないということです。

justify-content が効くのは「よこ」ではなく「主軸」です

フレックスには2本の軸があります。 子が並んでいく向きが主軸で、それと直角に交わるのが交差軸。 justify-contentは主軸に、align-itemsは交差軸に効きます。

flex-direction: rowのときは主軸がよこなので、justify-content: centerは「よこのまんなか」に見えます。 ところがflex-direction: columnに変えると主軸がたてに変わるので、同じ center が「たてのまんなか」になります。 指定は1文字も変えていないのに、効く向きだけが変わるわけです。

仕様は主軸の向きをこう決めています。rowのときの主軸は文字が進む向き(インライン方向)、columnのときは行が積まれる向き(ブロック方向)。 「よこ」「たて」と決め打ちしていないのは、縦書きでは文字が進む向きがたてになるからです。 日本語の縦書きのページでは、flex-direction: rowのまま子が上から下へ並びます。

上の図で、flex-direction を4つとも押してみてください。太い矢印(主軸)と細い矢印(交差軸)が入れかわります。row-reverseにすると主軸だけが裏返って、交差軸はそのままであることも確かめられます ── 交差軸の向きを変えるのはflex-wrap: wrap-reverseだけです。

order は見た目の順番だけ。読み上げの順番は変わりません

orderを書くと、HTMLを書き直さずに並び順を変えられます。 小さい数の子ほど先に並び、同じ数どうしはHTMLに書いた順のままです。 初期値は0なので、1つの子に -1 を付けるだけで先頭に出せます。

ただし、変わるのは見た目だけです。仕様は「order は、音声などの目で見ない媒体での順番を変えない」、「タブキーで移動していく順番も変えない」と、はっきり書いています。

つまり目で見た順番と、読み上げやキーボードで進む順番が食い違います。 画面を見ずに使っている人には、並べ替える前の順番で届くわけです。 見た目では2番目にあるボタンが、タブキーでは5番目に来る、ということが起こります。

だから仕様は、ここだけ強い言い方をしています。「order は見た目のための並べ替えにだけ使うこと。意味の順番を order で作ったスタイルシートは仕様違反である」。順番そのものに意味があるなら、HTMLの並びを直すのが正しいやり方です。

同じことがrow-reverseとcolumn-reverseにも言えます。 仕様は「これらは並び順を裏返しているのではなく、流れる向きを変えているだけで、 読み上げの順番もタブの順番も変わらない」と書いたうえで、「正しいHTMLの並びの代わりに order や -reverse を使ってはいけない。 文書のアクセシビリティを壊す」と念を押しています。

gap は「入れる」のではなく「あける」

子と子のあいだをあけたいとき、昔はmarginを使っていました。 でもmarginは両はしにも付くので、 「最後の1つだけ margin を消す」という手当てが必ずセットになります。

gapはあいだにだけすきまを作るので、 その手当てが要りません。 中身はrow-gapとcolumn-gapの省略形で、フレックスでは、主軸側が「子と子のあいだ」、交差軸側が「行と行のあいだ」になります。

書かないときの値はnormalです。数の0ではなくnormalという 言葉が入っていて、段組みでは1文字ぶん、それ以外では0として使われると 決められています。 フレックスは「それ以外」なので0です。 開発者ツールでgap: normalと出ていても、すきまは0だということです。

決まりの出どころは、省略形の展開・主軸と交差軸・折り返しの向き・reverse の注意書きがW3C の CSS Flexible Box Layout Module Level 1(新しいタブで開きます)、orderの定義と読み上げについての決まりがCSS Display Module Level 3(新しいタブで開きます)、justify-contentやgapの初期値がCSS Box Alignment Module Level 3(新しいタブで開きます)です。 このページに出てくる3つの値は、仕様の文をそのまま写したのではなく、実際のブラウザで測った値で、 測った結果と仕様が食い違っていないことを単体テストで毎回確かめています。