Flexboxを使っていて「なんでこの幅になるんだろう?」と疑問に思ったことはありませんか?
僕はFlexboxを使い始めたころ「flexアイテム」の幅がどうやって計算されるのかわからなくて、レイアウトにはまってしまいました。調べて整理したのでシェアします。
仕組みをしっかり理解しておくと、問題が起こったときに対処しやすくなります。ということで、flexアイテムの幅の計算方法、行ってみましょ〜!
まずは結論から — flexアイテムの幅の計算方法
結論からざっくり言ってしまいます。
display: flex
を指定した親要素を「flexコンテナ」、その中にある子要素を「flexアイテム」と呼びます。
上図のようにスペースが余っている場合、この「flexアイテム」にはflex-grow
の指定にしたがって余ったスペースが分配される仕組みになっています。
下図のようにflex-grow
(またはflex
)の合計で余白スペースを分割してflex-grow
(またはflex
)で指定した通りに分配されるようになっています。下図ではflexアイテムにflex: 1
とflex: 2
が指定してあるので、余ったスペースは3分割され、それぞれのflexアイテムに分配されます。
ちなみに、後で説明しますがflex
は複数のプロパティのショートハンドです。
HTMLとCSSの記述例
とりあえず、上図のように2カラムを1 : 2の幅でレイアウトする際のサンプル・コードを見てみてください。
HTMLの例
<div class="flex-container">
<div class="flex-1">
<p>Flex 1</p>
</div>
<div class="flex-2">
<p>Flex 2</p>
</div>
</div>
CSSの例
.flex-container {
display: flex;
}
.flex-1 { flex: 1; }
.flex-2 { flex: 2; }
こんなに簡単に思った通り1 : 2のカラム・レイアウトができてしまうわけですね。
これは先日見かけたPen で使われていた方法なんですが、「なぜ?」の部分が理解できませんでした。
まず、flex: 1
ってなに?
なんで値が1つでいいの? …という初歩的なところから疑問でした。
flexプロパティについて
調べてみたら、flex
プロパティは以下の3つのプロパティのショートハンドで、flex-shrink
とflex-basis
は省略できるんですね。
- flex-grow
- flex-shrink
- flex-basis
で、flex
の初期値(initial)はflex: 0 1 auto
flex: 1 0 auto(注)ということで、省略した値は初期値になるのかと思ったら違いました。
W3CのCSS Flexible Boxの仕様 を読んだら、flex
に1つだけ数値を指定すると、以下のようにflex-shrink
は1
に、flex-basis
は0
になるそうです。初期値(initial)はflex: 0 1 auto
flex: 1 0 auto(注)となっていたので混乱しました。省略された場合の値と初期値は別物なんですね。
flex: <number> 1 0;
仕様 にはflex: initial
、flex: auto
、flex: none
についてもしっかり説明があるので確認しておくと良いかもしれません。
注: 初期値(initial)の記述に誤りがありました
上述の初期値(initial)ですが、正しくはflex: 0 1 auto
だそうです。上の文章では値を修正してあります。
8月31日現在の最新の仕様のThe ‘flex’ shorthandの青い表 ではinitial
が1 0 auto
と書いてあるのですが、その下の文章中では「The initial values of the flex components are equivalent to flex: 0 1 auto.」とあり、こちらが正しい値のようです。さらに混乱させてしまってすみませんでした。
@JForgさんのこちらのTweet で知りました。ありがとうございました!
[追記: 2016/08/31 06:30]
注意: IE10-11のバグについて
IE10にはバグ があって、flex: 1
はflex: 1 0 0px
と解釈されてしまうとのことです(IE11では修正されたとのことです)。さらに、IE10-11では、flexショートハンドで記述した際にflex-basisの値に単位をつけないと正確に認識されないというバグ があるそうです。これを避けたかったら省略せずにflex: 1 1 0%
のように0でも%を記述しなくてはならないようです。
Advertisement
flexに数値を1つだけ指定した場合の解釈
ということで、さっきのflex: 1
はflex: 1 1 0
と同じということになります。
- flex-grow: 1
- flex-shrink: 1
- flex-basis: 0
flexアイテムの幅を0をベースに計算して、flexコンテナ内に余白がある場合、余白となる幅をコンテナ内にある子要素のflex-grow
の合計で割った値を1ユニットとして、それがflex-grow
の値にしたがって振り分けられます。
たとえば、flexコンテナの幅が900px
の場合、flexアイテム「A」のflex-grow
の値が1
、flexアイテム「B」のflex-grow
の値が2
だったとします。その場合、以下のような計算になります。
- Aの幅 = A自体のコンテンツ幅 + (1 × (900px – AとBのコンテンツ幅の合計) ÷ 3)
- Bの幅 = B自体のコンテンツ幅 + (2 x (900px – AとBのコンテンツ幅の合計) ÷ 3)
さっきの図の通りですね。
ちなみに、先ほどのCSSの例の場合、flex-1
とflex-2
のどちらもflex-basis
が0
になり、幅が0で計算されます。わかりやすいように、flexコンテナの幅が900px
の場合で計算してみます。
- flex-1の幅 = 0 + (1 × (900px – 0) ÷ (1 + 2)) = 300px
- flex-2の幅 = 0 + (2 × (900px – 0) ÷ (1 + 2)) = 600px
(1 + 2)のところは、flex-1
とflex-2
に指定されたflex-grow
の値の合計です。
無事に1 : 2のカラムが構築されているのがわかりましたね。
flex-basisに幅の指定がある場合の計算方法
念のためflex-basisに幅が指定されている場合でも同じように1 : 2のカラムが構築できるか計算をしてみます。例えば、.flex-1
には150px
、.flex-2
には300px
を指定してみます。
.flex-1 { flex: 1 1 150px; }
.flex-2 { flex: 2 1 300px; }
- flex-1の幅 = 150px + (1 × (900px – (150px + 300px)) ÷ (1 + 2)) = 300px
- flex-2の幅 = 300px + (2 × (900px – (150px + 300px)) ÷ (1 + 2)) = 600px
flex-1 : flex-2 = 1 : 2の幅になりますね。
ただ、これはレイアウトが1 : 2になるように幅の値(150 : 300 = 1 : 2)を指定した結果です。以下のように違う比率の値を指定すると、違った比率のレイアウトになります。たとえば、.flex-1
には420px
、.flex-2
には180px
を指定してみます。
.flex-1 { flex: 1 1 420px; }
.flex-2 { flex: 2 1 180px; }
- flex-1の幅 = 420px + (1 × (900px – (420px + 180px)) ÷ (1 + 2)) = 520px
- flex-2の幅 = 180px + (2 × (900px – (420px + 180px)) ÷ (1 + 2)) = 380px
幅の大きさも比率も、全然違うレイアウトになりますね。
flex-grow
はあくまで余白の分配方法を指定する数値なので、flex-basis
で指定した幅との組み合わせで、それぞれのflexアイテムの幅が計算されていることがわかります。
だいぶflexアイテムの幅の計算方法がわかってきました。
flexアイテムにpaddingの指定がある場合の計算方法
では、今度はflexアイテムにpadding
が指定されている場合はどうでしょうか?
これは以下の2つの要因によって計算が違ってきます:
- flex-basisの幅の指定
- box-sizingの値
flex-basisに幅の指定がありbox-sizingの指定がない場合
flex-basis
に幅が指定されていてbox-sizing
の指定がない場合(box-sizing: content-boxの場合)、flex-1
とflex-2
のそれぞれのコンテンツ幅(150pxと300px)にpadding
(10px × 2)が含まれないため、余白の計算にpadding
分の数値を足して計算されるようです。
- flex-1の幅 = 150px + (1 × (900px – (150px + 20px + 300px + 20px)) ÷ (1 + 2)) = 286.67px
- flex-2の幅 = 300px + (2 × (900px – (150px + 20px + 300px + 20px)) ÷ (1 + 2)) = 573.33px
図にすると、以下のようになります。
flex-basisに幅の指定があり、box-sizing: border-boxの指定がある場合
flex-basis
に幅が指定されていて、なおかつbox-sizing: border-box
を指定した場合、コンテンツの幅にpadding
が含まれるため、余白の計算の調整が必要ありません。そのため、計算はpadding
がない時と同じになります。
- flex-1の幅 = 150px + (1 × (900px – (150px + 300px)) ÷ (1 + 2)) = 300px
- flex-2の幅 = 300px + (2 × (900px – (150px + 300px)) ÷ (1 + 2)) = 600px
計算がシンプルになっていいですね。
flex-basisに幅の指定がない場合
さらに今度は、flex-basis
の指定がない場合(または0が指定されている場合)の計算はどうなるのか見てみます。これはbox-sizing
の指定のあり・なしに関係なく、同じ計算結果になります。(厳密に言うと、paddingが幅に含まれるか含まれないかでflexアイテムの幅はpadding分だけ異なりますが、見た目上の結果は同じになります。)
- flex-1の幅 = 0 + (1 × (900px – (10px × 4)) ÷ (1 + 2)) = 286.67px
- flex-2の幅 = 0 + (2 × (900px – (10px × 4)) ÷ (1 + 2)) = 573.33px
flex-basis
で幅を0
に指定しているのに、不思議な計算な気もしますけど。そういう仕様だということでしょうか?
「余白を計算する」という意味で、コンテンツに幅が発生する限り、その調整を行うということなのでしょうか?幅が0の要素に対するpadding
の論理上の扱いもbox-sizing: border-box
と合わせて考えると謎なような気もしますが。実際にHTMLとCSSを組んでみて、ブラウザで確認すると上に書いた計算結果と同じになります。
注意: IE10-11でのバグについて
IE10-11でflex-basis
に指定した幅にbox-sizing: border-box
が効かないというバグ があるそうです。これを回避するには、以下のHTML例のようにflexアイテムの中の要素にpadding
を指定するのが良さそうです。
HTMLの例
<div class="flex-container">
<div class="flex-1">
<p>Flex 1</p>
</div>
</div>
CSSの例
.flex-container {
display: flex;
}
.flex-1 { flex: 1; }
.flex-1 p { padding: 10px; }
確認に使ったブラウザ
この記事を書くために行ったブラウザでの確認は、MacのChrome 52.0.2743.116 (64-bit)とFirefox 46.0.1で行いました。
まとめ
これでようやくFlexboxを使ったグリッドシステムが作れそうな気がしてきました。Flexboxの特性を活かしたうえで、よりシンプルで柔軟なシステムを構築するヒントが得られたような気がしています。
Bootstrap 4 やFoundation 6 のようなメジャーなフレームワークでも導入されてますし、これからグリッドシステムを構築するならFlexboxで間違いないんだと思いますが。すべて人任せというのも納得がいかないので、せめて検証くらいは自分でしておこうと思います。
Flexboxの仕様はCRの状態 ですし、まだバグ も隠れていそうです。また、今回の検証は最新版のMac ChromeとFirefoxでしか確認していないので、まだ現場での実装には注意が必要だと思っています。でも、他のブラウザでテストしてポリフィルなどの実装が確認できたら、そろそろGOしちゃっても良さそうですね。
Flex GO!
更新情報
- イントロ部分の情報が古くなっていたので更新しました(2020/02/02)
2016年8月30日に公開され、2020年2月2日に更新された記事です。
About the author
「明日のウェブ制作に役立つアイディア」をテーマにこのブログを書いています。アメリカの大学を卒業後、ボストン近郊のウェブ制作会社に勤務。帰国後、東京のウェブ制作会社に勤務した後、ウェブ担当者として日英バイリンガルのサイト運営に携わる。詳しくはこちら。
ウェブ制作・ディレクション、ビデオを含むコンテンツ制作のお手伝い、執筆・翻訳のご依頼など、お気軽にご相談ください。いずれも日本語と英語で対応可能です。まずは、Mastodon @rriver@vivaldi.net 、Twitter @rriver 、またはFacebook までご連絡ください。
[…] Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法 – Rriver […]
[…] 6. CSS:Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法 この記事をRTする […]
大変有用な記事をありがとうございます!
下記の式ですが積・商の記述順が逆になっていると思いました。
flex-1の幅 = 0 + (1 × (900px – 0) ÷ (1 + 2)) = 300px
flex-2の幅 = 0 + (2 × (900px – 0) ÷ (1 + 2)) = 600px
flex-2の幅は、この計算だと900となってしまいます。
正しくは
flex-1の幅 = 0 + (1 × ((900px – 0) ÷ (1 + 2))) = 300px
flex-2の幅 = 0 + (2 × ((900px – 0) ÷ (1 + 2))) = 600px
または
flex-1の幅 = 0 + (900px – 0) ÷ (1 + 2) × 1 = 300px
flex-2の幅 = 0 + (900px – 0) ÷ (1 + 2) × 2 = 600px
となるかと思いました。
重箱の隅をつつくようで恐縮ですが、
拝読していて気がつきましたのでお伝えいたします。
ご指摘ありがとうございます。
計算は間違っていないですが、意味を考えるとコメントいただいたものの方がわかりやすいですね。
> flex-1の幅 = 0 + (900px – 0) ÷ (1 + 2) × 1 = 300px
> flex-2の幅 = 0 + (900px – 0) ÷ (1 + 2) × 2 = 600px
ありがとうございます。
1年越しで・・・
何か間違ったツッコミをしていてすみません!!
優しくご対応いただいており、大変恐縮です。
今後とも陰ながら、記事楽しみにしております。有難うございました。
大変に参考になるブログをありがとうございます。記事の中の図を使った説明ですが、図は何のアプリを使用して描いているのでしょうか? 私もこれから始めるブログ記事でこんな風に作成できたら嬉しいので、良かったら教えてください。
図の作成は、たしか、このブログでも紹介しているAffinity Designerだったと思います。現在キャンペーン中で90日間の無料トライアル と50%セールをやっているので、まだ使ったことがなかったらおすすめです。
貴重な情報をありがとうございます、早速使ってみたのですが、とっても使いやすくて、重宝できそうです。 合わせて、教えていただきたいのですが、ブログ用に画像として書き出す時のサイズと、Affinitey Designer の新規作成の時のボードのサイズはどれを使えばいいのでしょうか? あとブログの中のHTMLとCSSの書き出しはキャプチャーツールですか? 何度も申し訳ないのですが、よろしくお願いいたします。