Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法

Advertisement

how-flox-grow-works

Flexbox、使ってますか?

僕はまだなんですが、そろそろflexboxを使ってグリッド・システムを作りたいと思っていて実験中です。今までのカラム幅の指定には%やpx値を使ってきましたが、flexboxでは%やpxを指定しない方法を考えています。しなくて良い計算はしたくないですからね。

ところが、検証の開始そうそうに「flexアイテム」の幅がどうやって計算されるのかわからなくて、はまってしまいました。調べてみたので情報をまとめておきたいと思います。

今回の検証はMacのChrome 52.0.2743.116 (64-bit)とFirefox 46.0.1で確認をしています。あと、Flexboxの基本を理解していることを前提に書いているので、基本については以下あたりをご参照ください:

目次

まずは結論から — flexアイテムの幅の計算方法

結論からざっくり言ってしまいます。

display: flexを指定した親要素を「flexコンテナ」、その中にある子要素を「flexアイテム」と呼びます。

flex-container-and-item

上図のようにスペースが余っている場合、この「flexアイテム」にはflex-growの指定にしたがって余ったスペースが分配される仕組みになっています。

下図のようにflex-grow(またはflex)の合計で余白スペースを分割してflex-grow(またはflex)で指定した通りに分配されるようになっています。下図ではflexアイテムにflex: 1flex: 2が指定してあるので、余ったスペースは3分割され、それぞれのflexアイテムに分配されます。

flex-width-calculation

ちなみに、後で説明しますが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-shrinkflex-basisは省略できるんですね。

  1. flex-grow
  2. flex-shrink
  3. flex-basis

で、flexの初期値(initial)はflex: 0 1 autoflex: 1 0 auto)ということで、省略した値は初期値になるのかと思ったら違いました

W3CのCSS Flexible Boxの仕様 を読んだら、flexに1つだけ数値を指定すると、以下のようにflex-shrink1に、flex-basis0になるそうです。初期値(initial)はflex: 0 1 autoflex: 1 0 auto)となっていたので混乱しました。省略された場合の値と初期値は別物なんですね。

flex: <number> 1 0;

仕様 にはflex: initialflex: autoflex: noneについてもしっかり説明があるので確認しておくと良いかもしれません。

注: 初期値(initial)の記述に誤りがありました

上述の初期値(initial)ですが、正しくはflex: 0 1 autoだそうです。上の文章では値を修正してあります。

8月31日現在の最新の仕様のThe ‘flex’ shorthandの青い表 ではinitial1 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: 1flex: 1 0 0pxと解釈されてしまうとのことです(IE11では修正されたとのことです)。さらに、IE10-11では、flexショートハンドで記述した際にflex-basisの値に単位をつけないと正確に認識されないというバグ があるそうです。これを避けたかったら省略せずにflex: 1 1 0%のように0でも%を記述しなくてはならないようです。

Advertisement

flexに数値を1つだけ指定した場合の解釈

ということで、さっきのflex: 1flex: 1 1 0と同じということになります。

  1. flex-grow: 1
  2. flex-shrink: 1
  3. 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)

さっきの図の通りですね。

flex-width-calculation

ちなみに、先ほどのCSSの例の場合、flex-1flex-2のどちらもflex-basis0になり、幅が0で計算されます。わかりやすいように、flexコンテナの幅が900pxの場合で計算してみます。

  • flex-1の幅 = 0 + (1 × (900px – 0) ÷ (1 + 2)) = 300px
  • flex-2の幅 = 0 + (2 × (900px – 0) ÷ (1 + 2)) = 600px

(1 + 2)のところは、flex-1flex-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-1flex-2のそれぞれのコンテンツ幅(150pxと300px)にpadding(10px × 2)が含まれないため、余白の計算にpadding分の数値を足して計算されるようです。

flex-with-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-with-padding-calculation

flex-basisに幅の指定があり、box-sizing: border-boxの指定がある場合

flex-basisに幅が指定されていて、なおかつbox-sizing: border-boxを指定した場合、コンテンツの幅にpaddingが含まれるため、余白の計算の調整が必要ありません。そのため、計算はpaddingがない時と同じになります。

flex-with-padding-box-sizing-border-box
  • 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-with-padding-wo-flex-basis
  • 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; }

まとめ

これでようやくFlexboxを使ったグリッドシステムが作れそうな気がしてきました。Flexboxの特性を活かしたうえで、よりシンプルで柔軟なシステムを構築するヒントが得られたような気がしています。

Bootstrap 4 Foundation 6 のようなメジャーなフレームワークでも導入されてますし、これからグリッドシステムを構築するならFlexboxで間違いないんだと思いますが。すべて人任せというのも納得がいかないので、せめて検証くらいは自分でしておこうと思います。

Flexboxの仕様はCRの状態 ですし、まだバグ も隠れていそうです。また、今回の検証は最新版のMac ChromeとFirefoxでしか確認していないので、まだ現場での実装には注意が必要だと思っています。でも、他のブラウザでテストしてポリフィルなどの実装が確認できたら、そろそろGOしちゃっても良さそうですね。

Flex GO!

“Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法” への2件のフィードバック

  1. […] Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法 – Rriver […]

  2. […] 6. CSS:Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法 この記事をRTする […]

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です