flex-grow, flex-shrink, flex-basis について
flex-grow CSS プロパティは、flex アイテムの flex grow factor を指定します。これは、アイテムが flex コンテナ内のスペースをどれだけ占有するかを指定します。
MDN: flex-grow
MDN の説明がシンプルすぎてよく分からなかったので、もう少し詳しく調べた内容をまとめておきます。
説明中に出てくるスクショの動作サンプルは下の記事にあります。
この記事は上の動作サンプル記事が長くなっため分離した補足記事みたいな感じです。
flexbox
の基本
flexbox の基本的な動作は以下のページが個人的には分かりやすかったのでオススメです。
- CSS3 Flexbox の各プロパティの使い方をヴィジュアルで詳しく解説
flexbox の仕様全般に関する説明が分かりやすい - Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法
flex-grow に関する説明が分かりやすい
flex-grow
ある flex アイテムが、 flex コンテナ内の他の flex アイテムと比較して、どのくらい大きくなろうとするかを整数値で指定する。
W3C の flex grow
の説明にはこうある。
This
component sets flex-grow longhand and specifies the flex grow factor, which determines how much the flex item will grow relative to the rest of the flex items in the flex container when positive free space is distributed. When omitted, it is set to 1.
W3C: flex-grow
意味としては、
この数値は、ポジティブなフリースペースが分配された時に、 flex アイテムが flex コンテナ内の他の flex アイテムと比較してどれくらい増大されるかを決定します。
ということだと思う。
flex-grow
はあくまでも余ったスペースを各アイテムで分け合う。
分配されるのは領域全体ではなく、余ったスペース(以下ポジティヴなフリースペース)。
ある領域を1:2:3
の比率で分割したいとして、単純に flex-grow
をそれぞれ1
,2
,3
と指定しても上手くいかない。
上は width
を 60px
, 120px
, 180px
に指定した display: block
の要素。
下は flex-grow
を 1
, 2
, 3
に指定した display: flex
の要素。
上と下を見比べると、下の比率が1:2:3
になっていないことが分かる。
下の図について細く見ていくと、余ったスペースが各アイテムに対してどのように分配されているのかが分かる。
flex コンテナの width
は360px
。
flex アイテムの width
は auto
、 flex アイテム内のコンテンツは40px
の span
要素なので、 flex アイテムの幅は40px
となる。
flex-grow
が分け合うポジティヴなフリースペースとは、 flex コンテナから flex アイテムの幅を引いた値。360px - (40px + 40px + 40px) = 240px
のことを指す。
この240px
を flex-grow
の値1:2:3
の比率で分配し、 width
に足した合わせた値が最終的な幅となる。
(40 + 1/6 * 240) + (40 + 2/6 * 240) + (40 + 3/6 * 240)
80 + 120 + 160
この例では比率が2:3:4
になる。
flex-shrink
ある flex アイテムが、 flex コンテナ内の他の flex アイテムと比較して、どのくらい小さくなろうとするかを整数値で指定する。
W3C の flex-shrink
の説明にはこうある。
This
component sets flex-shrink longhand and specifies the flex shrink factor, which determines how much the flex item will shrink relative to the rest of the flex items in the flex container when negative free space is distributed.
W3C: flex-shrink
意味としては、
この数値は、ネガティブなフリースペースが分配された時に、 flex アイテムが flex コンテナ内の他の flex アイテムと比較してどれくらい縮小されるかを決定します。
ということだと思う。
ネガティヴなフリースペースというが分かりにくい。
flex コンテナの幅と flex アイテムの幅の差がポジティヴかネガティヴかというふうに考えると分かりやすいかもしれない。
flex-grow
の時と逆の状況を考える。
flex アイテムの width
を 160px
にして、 flex コンテナからはみ出すようにしてみる。
- flex アイテムを全て足した幅が flex コンテナより小さい
360px - (40px + 40px + 40px) = 240px
= ポジティブなフリースペース。 - flex アイテムを全て足した幅が flex コンテナより大きい
360px - (160px + 160px + 160px) = -120px
= ネガティヴなフリースペース
flex-shrink
はこのネガティヴなフリースペースをどう分配するか、ということを指定する。
下のスクショは、
上が display: block
+ float: left
、 width: 120px
の要素。
下が flex-shrink
を 1
,2
,3
に指定した display: flex
の要素となっている。
ネガティヴなフリースペース -120px
を、 flex-shrink
の値1:2:3
の比率で分配すると、
(160 + 1/6 * -120) + (160 + 2/6 * -120) + (160 + 3/6 * -120)
140 + 120 + 100
flex アイテムはそれぞれ140px
、120px
、100px
となり、比率は7:6:5
となる。
flex-basis
flex-basis
についてはまだ仕様が不安定な印象です。
とりあえず MDN の説明を引用。
flex-basis CSS プロパティは、flex アイテムの初期 main size である flex basis を指定します。box-sizing を使用して別に指定されていない限り、このプロパティが content-box の寸法を定義します。
MDN: flex-basis
要はベースとなる width
か height
を指定するということだと思う。
box-sizing
の影響を受けるので、 border
、 padding
を含めた値を指定したい場合は box-sizing
を border-box
にする。
(ただし、IEでは box-sizing
の指定に関するバグ有り。 Flexbugs: 7. flex-basis doesn't account for box-sizing:border-box)
指定出来る値は width
、 height
に設定出来る値であれば何でも良い。
あるいは、 content
を指定する。
content
結論から書くと、動作がブラウザによってバラバラなので、現段階では使わないほうが良さそうです(2016年9月4日)。
コンテンツサイズに応じてよしなにやってくれる的な設定なんじゃないかーぐらいのことしか分かりませんでした。
W3C の説明も
Indicates automatic sizing, based on the flex item’s content.
と一行だけです。
実際の動作サンプルについては別記事に追記があります。
メモを揉め: Case Studies in Flexbox - flex-grow, flex-shrink, flex-basis
ブラウザ間の差異の原因になっている(2016年9月4日現在)
flex-basis
は Chrome 、 Firefox 、 Edge グループと Safari 、 IE11 グループとで動作が異なる。
2017年8月17日現在は IE11 のみ動作が異なる。
Chrome 、 Firefox 、 Edge はコンテンツのサイズを維持。
Safari 、 IE11 はコンテンツサイズを無視する。
Chrome 、 Firefox 、 Safari 、 Edge はコンテンツのサイズを維持。
IE11 はコンテンツサイズを無視する。
こちらも詳しくは別記事に記述があります。
メモを揉め: Case Studies in Flexbox - flex-grow, flex-shrink, flex-basis
その他の考慮すべきこと
ここまでの flex-grow
、 flex-shrink
の動作は、余った、あるいは足りないスペースを flex-grow
、 flex-shrink
に基いて分配するという、ある意味シンプルなルールです。
しかし、場合によっては様々な要因によって結果が予想しにくくなることがあります。
再計算が必要になる場合
フリースペースの分配が行われた後、何らかの原因によって再びネガティブスペースを作ってしまった場合、再計算が行われます。
0以下になる場合
flex
コンテナの幅が 360px
、
flex
アイテムの flex-shrink
が 1
、 2
、 3
、
width
が 400px
、
min-wditn
が 0
に設定されていた場合、下のスクショの様な結果になる。
順を追って見ていくと、
flex
コンテナからflex
アイテム3つの合計を引く
360 - (400 + 400 + 400) = -840
- ネガティブなフリースペース
-840px
をflex-shrink
に基いて分配する
(400 - 1/6 * 840) + (400 - 2/6 * 840) + (400 - 3/6 * 840)
- 3つ目の
flex
アイテムが0
を下回ってしまう
(260) + (120) + (-20)
- 再び生まれたネガティブなフリースペース
-20px
の再分配が行われる
(260 - 1/3 * 20) + (120 - 2/3 * 20) + (0)
- 最終的なサイズが決定する
253.3333... + 106.6666... + 0
コンテンツサイズを下回る場合
Chrome 、 Firefox 、 Edge の flex-basis
の挙動により起こる現象。
flex
コンテナの幅が 360px
、
flex
アイテムの flex-grow
が 1
、 5
、 6
、
flex-basis
が 0
に設定されていた場合、下のスクショの様な結果になる。
順を追って見ていくと、
flex
コンテナからflex
アイテム3つの合計を引く
360 - (0 + 0 + 0) = 360
- ポジティブなフリースペース
360px
をflex-grow
に基いて分配する
(0 + 1/12 * 360) + (0 + 5/12 * 360) + (0 + 6/12 * 360)
- 1つ目の
flex
アイテムがコンテンツサイズの40px
を下回ってしまう
(30) + (150) + (180)
- 1つ目の
40px
を除いた320px
が再びポジティブなフリースペースとして再分配される(40) + (0 + 5/11 * 320) + (180 + 6/11 * 320)
- 最終的なサイズが決定する
40 + 145.4545... + 174.5454...
margin, paddingを下回る場合
margin
、 padding
は flex-grow
、 flex-shrink
によって増大も縮小もしないため、フリースペースの分配結果がこれらを下回る場合は再分配が行われる。
flex
コンテナの幅が 360px
、
flex
アイテムの flex-shrink
が 1
、 2
、 3
、
width
が 160px
、
左右の padding
が 40px
、
min-width
が 0
に設定されていた場合、下のスクショの様な結果になる。
flex
コンテナからflex
アイテム3つの合計を引く
360 - (240 + 240 + 240) = -360
- ポジティブなフリースペース
360px
をflex-grow
に基いて分配する
(240 - 1/6 * 360) + (240 - 2/6 * 360) + (240 - 3/6 * 360)
- 3つ目の
flex
アイテムが左右のpadding
の合計80px
を下回ってしまう
(180) + (120) + (60)
- 再び生まれたネガティブなフリースペース
-20px
の再分配が行われる
(180 - 1/3 * 20) + (120 - 2/3 * 20) + (80)
- 最終的なサイズが決定する
173.3333... + 106.6666... + 80
それでも解決しない場合
再分配しても以下の様に、
0
を下回るmin-width
を下回る- margin, paddingを下回る
- コンテンツサイズを下回る
に当てはまる場合は、サイズが下回らない様にコンテナからはみ出し、分配はそこで終わりとなる。
flex
コンテナの幅が 360px
、
flex
アイテムの flex-shrink
が 1
、 2
、 3
、
width
が 0px
、
左右の padding
が 70px
に設定されていた場合、下のスクショの様な結果になる。
左右の padding
の合計 140px
より縮小することが出来ないため、 flex コンテナからはみ出る。