search
LoginSignup
2

posted at

フレックスアイテムのflex-basisとwidthプロパティの関係を理解する

フレックスアイテムのflex-basiswidthプロパティ。ブラウザがアイテムの寸法を決定する際、この2つのプロパティ値はどのように作用するのでしょうか? よく理解できていなかったのでサンプルを作って検証してみました。
flex_all_auto_px.gif
ソース: https://codepen.io/kaz_hashimoto/pen/vYewQOK

今回作ったサンプルは、以下のプロパティ値を組み合わせたパターンごとにフレックスボックスの表示をまとめて確認できるので、自分用のチートシートにもなります。

プロパティ
flex-grow 0, 1
flex-shrink 0, 1
flex-basis px値, %値, auto
width px値, auto

使用するフレックスボックスは3個のアイテムからなるコンテナーで、主軸は行方向です。

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>

アイテムのwidthに指定するサイズは、px値、100%, autoの3種類です。px値については、3つの子要素の幅をそれぞれ50px, 100px, 150pxに設定してあります。下図は通常の(flexでない)レイアウトで表示した場合です。
initial_400.png
アイテムの背景に引かれたピンクの縦線の間隔は50pxです。また、コンテナーの下側 — ピンク色の境界線は、コンテナーの現在の幅を表す“インジケーター”です。

コンテナーの幅は画面右上のrange sliderを使って調節できます。初期値は400pxです。

css
.box {
  width: 400px;
  display: flex;
}

.box > * {
  flex: 1 1 auto; /* 例 */
}

flex-basisにpx値を指定した場合

flex: 0 0 px値

最初の例として、アイテムの幅がフレキシブルに変化しないケースから見ていきましょう。
flex-growflex-shrinkに0を指定して、flex-basisにpx値を指定した場合です。 図で2カラム目に"width: (px)"と表記したものは、3つのアイテムに幅50px, 100px, 150pxをそれぞれ設定したことを表します。
flex0_0_px_width.gif
このサンプルのケースでは、アイテムに設定したwidthがpx値かautoかに関係なく
flex-basisが80pxの時は最終的なサイズは3つとも80pxに計算され、flex-basisが0の時は各アイテムのコンテントの最小幅に縮んでいるように見えます。

なぜこのような結果になるのか、CSS Flexible Box Layout Module Level 1に記載されているアイテムのサイズ計算のロジックを追ってみました。サイズ計算に使われるパラメーターを抜き出してそれらの関係をチャートにするとこんな感じです。
chart_1920.png
なお、サイズ計算の説明を簡単にするため、フレックスコンテナーの主軸は行方向とし、交差軸のサイズ計算は考慮せず、アイテムのその他のプロパティ値については下表のとおりとします。

プロパティ 備考
box-sizing border-box
min-width auto 初期値
max-width none 初期値
flex-wrap nowrap 初期値

まず、アイテム"Three"について、flex-basis: 80px, width: 150pxの場合のサイズ計算を上記のチャートの変数x1〜x9にあてはめてみます。

No. 項目 備考 section
x1 content size suggestion 62px 4.5
x2 specified size suggestion 150px 4.5
x3 content-based minimum size 62px min(x1, x2) 4.5
x4 flex base size 80px 9.2の3.A
x5 min main size 62px min-width = x3 4.5
x6 max main size max-width: none
x7 hypothetical main size 80px clamp(x5, x4, x6) 9.2の3
x8 target main size 80px = x7 9.7の2
x9 main size 80px = x8 9.7の5

アイテムの最終的なサイズは確かに80pxと算出されました。
この表で変数x1の値は、サンプル図の3、4行目に表示されているアイテム"Tree"のdivの幅をDevToolsで取得したものです。表記を簡単にするため整数に丸めました。

ポイントは変数x5: min main sizeです。これはmin-widthのことで、今、初期値autoです。Flexible Box Layout Moduleにおけるautoautomatic minimum sizeで説明されているcontent-based minimum size、つまり変数x3の値になります。

次にアイテム"Three"について、flex-basis: 0px, width: 150pxの場合の計算表です。

No. 項目 備考 section
x1 content size suggestion 62px 4.5
x2 specified size suggestion 150px 4.5
x3 content-based minimum size 62px min(x1, x2) 4.5
x4 flex base size 0px 9.2の3.A
x5 min main size 62px min-width = x3 4.5
x6 max main size max-width: none
x7 hypothetical main size 62px clamp(x5, x4, x6) 9.2の3
x8 target main size 62px = x7 9.7の2
x9 main size 62px = x8 9.7の5

変数x4: flex base sizeは0ですが、x5: min main sizeの62pxで下側がclampされるため、最終的なサイズも62pxになります。

この2つのパターンを押さえれば、flex-shrinkflex-growが1の場合に起きるサイズの伸縮を理解できます。前述のチャート右下の9.7. Resolving Flexible Lengths(変数x7 → x8)を通じてコンテナーのフリースペースがアイテムに配分されます。

flex: 0 1 px値

flex-shrinkが1の場合、コンテナーにフリースペースが残っている時は、各アイテムのサイズは「flex: 0 0 px値」の図と同じ状態になります。コンテナーの幅がアイテムのhypothetical main size(変数x7)の合計より短くなるとflex-shrinkが発動してアイテムのサイズが最小サイズまで縮まります。
flex0_1_px_width.gif

flex: 1 0 px値

flex-growが1の場合、コンテナーにフリースペースがある限り各アイテムに配分し尽くされるのでコンテナの幅一杯にアイテムで埋められます。コンテナーの幅がアイテムのhypothetical main size(変数x7)の合計より短い場合は、「flex: 0 0 px値」の図と同じ状態になります。
flex1_0_px_width.gif

flex: 1 1 px値

flex-grow, flex-shrink共に1の場合は、上記2つを併せた動きになります。つまり、shrinkが発動する時はflex: 0 1 px値のパターンに、growが発動する時はflex: 1 0 px値のパターンに帰着します。
flex1_1_px_width.gif

flex-basisにautoを指定した場合

flex: * * auto; width: px値

今度はflex-basisautoを指定してみましょう。まずwidthがpx値の場合は下図に示した動きになります。
flex_all_auto_px.gif
これらのflex-basisの使用値(ピクセル数)はどこの寸法から取得しているのでしょうか?
1行目のアイテム"Three"に着目して、flex: 0 0 auto; width: 150pxの場合の計算表を埋めてみます。

No. 項目 備考 section
x1 content size suggestion 62px 4.5
x2 specified size suggestion 150px 4.5
x3 content-based minimum size 62px min(x1, x2) 4.5
x4 flex base size 150px = width 7.1
x5 min main size 62px min-width = x3 4.5
x6 max main size max-width: none
x7 hypothetical main size 150px clamp(x5, x4, x6) 9.2の3
x8 target main size 150px = x7 9.7の2
x9 main size 150px = x8 9.7の5

7.1. The flex Shorthandによると、<flex-basis>autoの場合はmain sizeプロパティ(主軸側、ここではwidth)の値をflex-basisの使用値として取得することがわかります。

auto
When specified on a flex item, the auto keyword retrieves the value of the main size property as the used flex-basis. If that value is itself auto, then the used value is content.

したがって、変数x4: flex base sizeの値は150pxになり、これが変数x7のclamp()もパスして最終的なサイズになるのがわかります。

flex: * * auto; width: auto

次にwidthautoの場合です。

flex_all_auto_auto.gif

これも1行目のアイテム"Three"に着目して、flex: 0 0 auto; width: autoの場合の計算表を埋めてみます。

No. 項目 備考 section
x1 content size suggestion 62px 4.5
x2 specified size suggestion undefined width: autoの場合はundefined 4.5
x3 content-based minimum size 62px min(x1, x2) 4.5
x4 flex base size 62px max-content inline size 9.2の3.E
x5 min main size 62px min-width = x3 4.5
x6 max main size max-width: none
x7 hypothetical main size 62px clamp(x5, x4, x6) 9.2の3
x8 target main size 62px = x7 9.7の2
x9 main size 62px = x8 9.7の5

前述の7.1. The flex Shorthand<flex-basis>autoの場合の説明で、main sizeプロパティ(width)から取得した値もautoのときは使用値はcontentになります。

If that value is itself auto, then the used value is content.

で、contentとはフレックスアイテムの中身に基づく自動のサイズを示します。これはmax-content inline sizeのことで、例えばテキストの場合、soft wrapなしの状態でテキストの長さにフィットする最も狭いインラインサイズです。

content
Indicates an automatic size based on the flex item’s content. (It is typically equivalent to the max-content size, (以下略)

したがって、変数x4: flex base sizeの値は、テキスト"Three"がフィットする幅62pxとなります。これが変数x7のclamp()もパスして最終的なサイズになるのがわかります。

min-contentとmax-contentの違い

これまでの例ではアイテムの内容が1ワードのみのテキストだったため、min-contentとmax-contentの違いがわかりません。そこで、3つ目のアイテムのテキストを3ワードの長いテキストに置き換えてみましょう。

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three looooong words</div>
</div>

アイテム"Three.."に着目すると、1行目と3行目がテキストのsoft wrapなしの状態、つまりmax-content inline sizeの幅になっています。一方、2行目と4行目はflex-shrinkが発動し、テキストはsoft wrap可能な箇所はすべて折り返された状態、つまりmin-content inline sizeの幅になっています。

flex_auto_auto_long_900.png

アイテム"Three ..."について、flex: 0 0 auto; width: autoの場合の計算表を埋めてみます。

No. 項目 備考 section
x1 content size suggestion 88px 4.5
x2 specified size suggestion undefined width: autoの場合はundefined 4.5
x3 content-based minimum size 88px min(x1, x2) 4.5
x4 flex base size 182px max-content inline size 9.2の3.E ,
x5 min main size 88px min-width = x3 4.5
x6 max main size max-width: none
x7 hypothetical main size 182px clamp(x5, x4, x6) 9.2の3
x8 target main size 182px = x7 9.7の2
x9 main size 182px = x8 9.7の5

ここで、

  • 変数x1: min-content inline size : soft wrapした場合のサイズ(図の2, 4行目)
  • 変数x4: max-content inline size : soft wrapをしない場合のサイズ(図の1, 3行目)

そして変数x4の182pxが以降のclamp()もパスして最終的なサイズになります。

flex-basisにパーセント値を指定した場合

最後にflex-basisにパーセント値を指定した場合です。これはアイテムの包含ブロックのサイズ(この場合は幅)に対するパーセント値なので、「flex-basisにpx値を指定した場合」における「px値」を「包含ブロックの幅」に置き換えたものに帰着します。つまり、変数x4: flex base sizeはdiv.boxの幅に、変数x7: hypothetical main sizeはx4をアイテムの最小幅と最大幅でclamp()したサイズになります。

以下に示した2つの例はflex-basisに100%を指定した場合です。ここではアイテムのhypothetical main sizeの合計がdiv.boxの幅より大きくなるため、アイテムにflex係数が指定されている場合はflex-shrinkが発動して収縮します。
(詳細: 9.7. Resolving Flexible Lengths

flex: * * 100%; width px値

flex_all_pct_px.gif

flex: * * 100%; width: auto

flex_all_pct_auto.gif

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2