CSSのflex-basisとwidthで幅が変わるのはどんなときか

​​CSS​Flexbox,HTML,Playwright

flex アイテムの幅は、flex-basis で決めても width で決めても、伸びるときと縮むときの幅はほとんど同じになります。違いが出るのは、URL や長い英単語のような折り返せない中身が、指定した幅より長いときです。width は指定した幅を守って中身をはみ出させ、flex-basis は中身の幅まで広がります。横並びのカードやサイドバーの幅を決めるときに、どちらを書けばよいかの判断に使えます。

補足

検証環境は Ubuntu 24.04、Node.js 22.23.4、Playwright 1.63.0、Chromium 153 です。HTML と CSS だけのページを Playwright で開き、getBoundingClientRect() で要素の幅を測りました。フォントは Noto Sans CJK JP の 16px で、アイテムには box-sizing: border-box を付けています。Firefox と Safari は試していません。

伸びるときも縮むときも、幅の計算は同じだった

2つのアイテム a と b を横に並べて、width で大きさを決めた場合と flex-basis で決めた場合を比べました。

html
<div class="row">
  <div class="a">A</div>
  <div class="b">B</div>
</div>
css
.row {
  display: flex;
  width: 600px; /* 縮むときは 300px */
}

.a {
  width: 100px; /* ここを flex-basis: 100px に変えて比べる */
}

伸びるときは、行の幅を 600px にして、a と b に flex-grow: 1 を付けました。縮むときは、行の幅を 300px にして、flex-shrink は初期値の 1 のままにしました。

場面 a と b に付けた指定 a の幅 b の幅
伸びる(行 600px) a:width: 100px、b:width: 200px 250px 350px
伸びる(行 600px) a:flex-basis: 100px、b:flex-basis: 200px 250px 350px
伸びる(行 600px) a:width: 100px、b:flex-basis: 200px 250px 350px
縮む(行 300px) a:width: 400px、b:width: 200px 200px 100px
縮む(行 300px) a:flex-basis: 400px、b:flex-basis: 200px 200px 100px
縮む(行 300px) a:width: 400px、b:flex-basis: 200px 200px 100px

どちらで書いても、混ぜて書いても、幅は同じでした。伸びるときは、余った 300px を半分ずつ足して 250px と 350px です。縮むときは、はみ出した 300px を元の大きさの比 2:1 で割り振り、a が 200px、b が 100px 縮みました。

同じになるのは、flex-basis の初期値が auto だからです。Flexbox の仕様にある flex-basis の auto の定義では、auto のときは並べる向き(主軸)の大きさのプロパティ、つまり横並びなら width、縦並び(flex-direction: column)なら height の値が、伸び縮みの元になる大きさとして使われます。width: 100px だけを書いても、flex-basis: 100px と書いても、計算の出発点は同じ 100px になります。

両方を書くと flex-basis が勝つ

同じアイテムに width と flex-basis を両方書いたときは、flex-basis の値で幅が決まりました。行の幅は 600px で、flex-grow は初期値の 0 です。

書いた指定 幅
width: 300px だけ 300px
width: 300px; flex-basis: 100px 100px
flex-basis: 100px; width: 300px(順番を入れ替え) 100px
width: 100px; flex-basis: 300px 300px
width: 300px; flex-basis: auto 300px
width: 300px; flex-basis: content 9.7px

書く順番を入れ替えても、結果は変わりませんでした。別のプロパティなので、後に書いたほうが勝つという話ではありません。MDN の flex-basis の説明にも、flex-basis が auto 以外で width も設定されているときは flex-basis が優先される、とあります。flex-basis: content にすると width は使われず、中身の「A」1文字の幅(9.7px)になりました。

中身が長いと、width ははみ出し、flex-basis は広がる

違いが出たのは、アイテムの中身が指定した幅より長いときです。a の中身を折り返せない URL(https://example.com/articles/2026、幅 257.8px)にして、a に 150px を指定しました。行の幅は 600px で、flex-grow は初期値の 0 なので、a は伸びない設定です。

2段の横並び。上の「a に width: 150px」は a の枠が 150px で、URL の後半が枠の外へはみ出して b の「B」に重なっている。下の「a に flex-basis: 150px」は a の枠が URL の長さ(257.8px)まで広がり、B はその右に並んでいる
a に width: 150px と flex-basis: 150px を付けて比べたところ。数字は測った幅。画像は行の幅 400px。a は伸びない設定なので、a の幅は 600px のときと同じ
a に付けた指定 a の幅 中身が a の枠からはみ出すか
width: 150px 150px はみ出す
flex-basis: 150px 257.8px はみ出さない
flex-basis: 150px; min-width: 0 150px はみ出す
flex-basis: 150px; overflow: hidden 150px はみ出す(見えなくなる)

width: 150px では、a は 150px のままで、URL は枠の外へはみ出して隣の b に重なりました。flex-basis: 150px では、伸びない設定なのに a が URL の幅の 257.8px まで広がりました。

この差は、flex アイテムの min-width の初期値 auto から来ています。Flexbox の仕様の自動最小サイズでは、flex アイテムが狭くなれる下限を、中身をいちばん細くした幅にしています。ただし、width(縦並びなら height)に幅が指定されていれば、下限はその幅で頭打ちになります。この頭打ちに使われるのは width だけで、flex-basis の値は使われません。

  • width: 150px のとき:下限は、中身の 257.8px と width の 150px のうち小さい 150px。a は 150px より広がらないので、中身がはみ出す
  • flex-basis: 150px のとき:width は auto なので、下限は中身の 257.8px。150px から始まっても下限に引き上げられ、257.8px になる

flex-basis のまま 150px に収めたいときは、min-width: 0 で下限を取り払います。overflow: hidden を付けた場合も、仕様にあるとおり下限が 0 になり、150px になりました。ただしこちらは、はみ出した URL の後半が見えなくなります。min-width: 0 で直すときの注意点は、別の記事「CSSのflexで子要素のはみ出しをmin-width: 0で直す方法」で確かめています。

行の幅を変えると、伸び縮みする範囲がずれる

a と b の両方に flex-grow: 1 と flex-shrink: 1 を付けて、伸びも縮みもする設定にしました。a の中身は上と同じ URL、b の中身は「B」です。2つに同じ 150px を width で付けた場合と flex-basis で付けた場合で、行の幅を 100px から 700px まで 25px ずつ変えて、a の幅を測りました。

折れ線グラフ。横軸は行の幅(100〜700px)、縦軸は a の幅。灰色の線(width: 150px)は行の幅 300px までは 150px で平らで、そこから右上がりになる。水色の線(flex-basis: 150px)は行の幅 500px までは 257.8px で平らで、500〜525px のあいだで折れ、そこから先は灰色の線と重なって右上がりになる
行の幅を変えたときの a の幅。灰色が width: 150px、水色が flex-basis: 150px
行の幅 width: 150px のときの a flex-basis: 150px のときの a
200px 150px 257.8px
300px 150px 257.8px
400px 200px 257.8px
500px 250px 257.8px
550px 275px 275px
700px 350px 350px

行が広いときは、どちらも行の幅のちょうど半分になりました。元の大きさがどちらも 150px なので、余った幅を半分ずつ受け取るからです。

狭くなっていくと、width のほうは行の幅 300px で 150px に達し、それより狭くしても 150px から縮みませんでした。下限が width の 150px だからです。flex-basis のほうは、行の幅が 515.6px(257.8px の2倍)より狭くなると、半分の幅が中身の 257.8px を下回るので、257.8px で止まりました。25px 刻みの測定では、500px で 257.8px、525px で 262.5px でした。行の幅 400px では、width なら 200px ですが、flex-basis なら 257.8px です。同じ 150px を指定していても、狭い画面では幅が変わります。

縦並びでは、flex-basis は高さになる

flex-direction: column で縦に並べると、2つの違いはもっとはっきりします。コンテナーの幅を 600px、高さを 400px にして、a に指定を1つだけ付けました。

css
.row {
  display: flex;
  flex-direction: column;
  width: 600px;
  height: 400px;
}

.a {
  width: 100px; /* ここを flex-basis: 100px や height: 100px に変えて比べる */
}
a に付けた指定 a の幅 a の高さ
width: 100px 100px 24px
flex-basis: 100px 600px 100px
height: 100px 600px 100px

width: 100px は、縦並びでもそのまま横幅に効きました。一方、flex-basis: 100px は高さの 100px になり、height: 100px と同じ結果です。このとき幅が 600px なのは、横幅を指定していない a がコンテナーの幅いっぱいに広がったためです。仕様の主軸の大きさ(main size)の定義のとおり、flex-basis は並べる向き(主軸)の大きさを決めるので、横並びなら幅、縦並びなら高さになります。width は向きに関係なく、いつも横幅です。

max-width はどちらにも効く

max-width: 200px と一緒に書くと、width: 400px でも flex-basis: 400px でも flex: 1 でも、幅は 200px で止まりました。上限を決めたいときは、width と flex-basis のどちらを使っていても max-width が使えます。

どちらを書くかの目安

中身が長いときに中身に合わせて広がってほしいなら flex-basis、指定した幅より広がってほしくないなら width が向いています。ただし width でも、flex-grow を付けていれば余った幅の分だけ伸び、行の幅が足りなければ(flex-shrink の初期値 1 のまま)縮みます。また、はみ出した中身の扱いは別に決める必要があります。なお MDN では、flex-basis を単独で書くより、flex の一括指定を使うことが勧められています。flex の一括指定の展開のされ方は、別の記事「CSSのflex: 1は何の略かと、要素が伸び縮みするしくみ」で確かめています。

​Posted by 86