WWW Watch

CSS Flexible Box (Flexbox) を使ってみよう - 最新仕様対応版

スクリーンサイズや可変するウィンドウサイズなどに柔軟に対応するレイアウトが可能な CSS Flexible Box について、最新の W3C 仕様に基づいた解説をサンプルソースを使いながらやってみます。

CSS Flexible BoxCSS3 のモジュールとして策定されている CSS Flexible Box (CSS3 Flexbox) は、CSS によるレイアウトを実現するための仕組み。

レイアウトのための CSS プロパティは他にもありますが、CSS Flexible Box は特にスクリーンサイズや可変するウィンドウサイズなどに柔軟に対応した、その名の通りフレキシブルなレイアウトが可能です。

現在最新の仕様書は下記。2012年 9月 18日付けで勧告候補になっています。

なぜ今 CSS Flexible Box なのか

CSS Flexible Box は仕様的に新しいものではありませんが、最初に草案が公開されてから勧告候補になる現在までに数回、仕様が大きく変更されています。

結果としてブラウザの実装もなかなか足並みが揃わない状況が続いていました (IE10 のようにせっかく実装したら仕様が変わっちゃったなんていう例も) が、Opera 12.10 以降や、最近リリースされた IE11 が最新の仕様で正式にサポートしているほか、Firefox も 6月後半にリリースされたバージョン 22 からデフォルトで CSS Flexible Box 有効化 (ただし一部未実装の機能あり) するなど、かなりブラウザ側の実装も進んできたので、この辺で最新の仕様に基づいた解説でも書いておこうかなということで。

サンプルソースで学ぶ CSS Flexible Box

では早速試してみましょう。仕様を上から翻訳していってもあまりわかりやすくない気がするので、下記のサンプルとなる HTML を使って、CSS Flexible Box でレイアウトを作りながら具体的に解説していこうと思います。

ということで、使用する HTML は下記の通り。この HTML ファイルには手を加えず、CSS の方を変更していって、どのようにレイアウトが変わるかを見てみましょう。

<div id="main">
 
 <div class="content">
  <section>
   <h1>section 1</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 2</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 3</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 4</h1>
   <p>text ...</p>
  </section>
 </div>
 
 <div class="content">
  <section>
   <h1>section 5</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 6</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 7</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 8</h1>
   <p>text ...</p>
  </section>
 </div>
 
 <div class="content">
  <section>
   <h1>section 9</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 10</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 11</h1>
   <p>text ...</p>
  </section>
  <section>
   <h1>section 12</h1>
   <p>text ...</p>
  </section>
 </div>
 
</div>

あと、サンプルでは border を指定してわかりやすくしています。

#main {
  border: 1px dotted #f0f;
  padding: 1em;
}
.content {
  border: 1px dotted #0ff;
  padding: 1em;
}
section {
  border: 1px dotted #f00;
  padding: 1em;
}

ここまで指定した HTML をブラウザで表示すると下記のようになりますね。ごく普通に縦に要素が並んだ状態です。

CSS Flexible Box を適用する前のサンプル HTML

実際のページは下記。

display: flex; を指定してみよう

CSS Flexible Box では、レイアウトしたい要素の親要素に対して、display: flex;display: inline-flex; という指定もあります) を指定することで、その子要素が 「Flex item」、つまり 「フレキシブルにレイアウトされる要素」 として扱われます。一方、display: flex; を指定した要素は 「Flex container」 と呼ばれます。

float プロパティなどがレイアウトしたい要素自体に指定するのに対して、親要素に指定するという点がちょっと特殊ですが、逆に言えば、親要素 1つに CSS を適用するだけで、その子要素をまとめて簡単にレイアウトすることが可能です。

まず、div.content を横並びにレイアウトする場合を想定してみましょう。div.content の親要素は div#main になりますので、こいつに CSS を指定します。

display プロパティ自体は使い慣れたプロパティだと思いますが、値として、「flex」 を使用し、div#main に対して下記のように指定してみましょう。

#main {
  display: -webkit-flex;
  display: flex;
}

Google Chrome も、バージョン 21 以降で、ベンダプレフィックス付きながら実装はされていますので、今回のサンプルでも一応記述しておきます。

すると、この簡単な指定だけで、div.content が横並びにレイアウトされたのがわかります。

display: flex; を指定した状態

実際のページは下記で確認できます。ただし、対応するブラウザ以外では 「ステップ 1」 と変わらないと思いますが。

ウィンドウサイズを変更すれば、各カラムの横幅も、それに連動して可変すると思います。

横方向のレイアウトか縦方向のレイアウトか

次に、flex-direction プロパティを使ってみましょう。このプロパティは、レイアウトの方向を指定します。指定できる値は下記の通り。

  • row : 横方向 (初期値)
  • row-reverse : 横方向、ただしレイアウトの基点を通常とは逆に
  • column : 縦方向
  • column-reverse : 縦方向、ただしレイアウトの基点を通常とは逆に

先ほどの div#main に、下記のように追加してみます。

#main {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: row-reverse;
  flex-direction: row-reverse;
}

すると、並び順が変わっているのがわかりますか? また、サンプルのウィンドウサイズを大きくしていってもらうとわかりやすいですが、レイアウトの起点が 「右端」 になっている (row の時は起点が左端) のがわかると思います。

flex-direction: row-reverse; を指定した状態

実際のページは下記で確認できます。

さらに、縦方向のレイアウトでも試してみましょう。今度は div.contentdisplay: flex; を指定して、子要素の section 要素を 「Flex item」 として扱ってみましょう。具体的には下記のように指定します。

.content {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column;
  flex-direction: column;
}

ただ、これだと 「ステップ 3」 と見た目的には変わらないので、下記のようにしてレイアウトが変えられることを確認してみます。

.content {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column-reverse;
  flex-direction: column-reverse;
}

すると今度は、縦方向のレイアウトに関しても、起点が 「上端」 を起点ではなく、「下端」 を起点に要素がレイアウトされているのがわかります。

flex-direction: column-reverse; を指定した状態

実際のページは下記で確認できます。

order プロパティで並び順を指定

さて、ソースコードの方はわかりやすいように flex-direction: row-reverse;flex-direction: column-reverse の指定を、flex-direction: row;flex-direction: column に戻しておきます。

次に、order プロパティを試してみましょう。このプロパティは、さらに細かく並び順を指定できます。例えば、各 div.content 内にある section 要素のうち、それぞれ 2番目の子要素となる section 要素を対象に下記のような指定をしてみましょう。

.content section:nth-child(2) {
  -webkit-order: -1;
  order: -1;
}

各 Flex item は順序グループを持っていて、その初期値は 「0」 です。つまり、何も指定しなければ、すべての Flex item は 「0番目の順序グループ」 に属していることになります。

上記の例では、2番目の section 要素だけ、「-1」 という順序グループを与えたことになりますので、他の 0番目の順序グループに属する要素より先にレイアウトされたというわけです。

order: -1; を指定した状態

実際のページは下記で確認できます。

このように、ソースコードの記述順に関係なく、柔軟なレイアウトが行えるのも、CSS Flexible Box の特徴です。

各要素の横幅をコントロール

レイアウトの基本的なところはわかったと思いますが、次に、横並びになっている各要素の横サイズをコントロールする方法について触れてみます。

今の時点でも、Flex item になっている要素の内容と、borderpadding を含めてうまいことレイアウトしてくれているのがわかると思いますが、例えば、各 div.content の横サイズを親要素の横サイズ内で均等に振り分けたい場合、どのようにすれば良いでしょう。

こういう場合に便利なプロパティ、flex プロパティを使ってみます。flex プロパティの値は下記のように指定します。

flex: <'flex-grow'> <'flex-shrink'> <'flex-basis'>;

なお、flex-growflex-shrink は省略可能で、省略した場合の初期値は 「1」、flex-basis を省略した場合の初期値は 「0px」 です。

ちなみに、flex プロパティはショートハンドプロパティですので、flex-grow プロパティ、flex-shrink プロパティ、flex-basis プロパティでそれぞれ個別に指定することもできます。

それぞれの詳しい説明は後でするとして、とりあえず div.content に対して下記のように指定を追加してみましょう。

.content {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column;
  flex-direction: column;
  -webkit-flex: 1;
  flex: 1;
}

この状態は、flex-shrinkflex-basis を省略して、flex-grow のみ指定した状態ですが、これで各 div.content の横サイズが div#main 内で均等に割り振られたと思います。

flex: 1; を指定した状態

実際の画面は下記で確認してみてください。

また、今度は下記のように div#main 内で 2番目にある div.content に対して frex プロパティの指定を足してみます。

.content:nth-child(2) {
  -webkit-flex: 2;
  flex: 2;
}

すると今度は真ん中の div.content と左右の div.content の横サイズが 2:1 で割り振られていると思います。

flex プロパティの指定を追加した状態

これも実際の画面は下記で確認してみてください。

flex-growflex-shrink

flex プロパティの flex-growflex-shrink は、余白の割り振りについて指定する値です。flex-grow は拡大方向、逆に flex-shrink は縮小方向の余白を指定します。

flex-grow は、例えば親要素 (Flex container) の横サイズが 1000px、その中に 3つの子要素 (Flex item) があってそれぞれの横サイズが 200px だった場合、余りの 400px (1000px - 200px × 3) を各子要素に振り分けて、横サイズを拡大し、親要素内いっぱいに収まるようにレイアウトする際に、どういう割合でその余白を振り分けますかという指定になります。

つまり、「ステップ 6」 のように指定すれば、各要素に 1:1:1 で 余白の 400px を割り振りますし、「ステップ 7」 のように指定すれば、1:2:1 で割り振るということになります。

flex-shrink は、flex-basis に 「0」 以外の値が指定されていないと意味はないのですが、子要素の横サイズに対して親要素の横サイズが足りないとき、どこまで縮小するかを指定します。

flex-basis

flex-basiswidth プロパティを指定するのと同じで、Flex item の横サイズを指定します。ここで指定した横サイズになるべく近づくように、レイアウトされます。ですので、500px などと指定しても、実際に 500px の横サイズで描写されるわけではなく、そのサイズになるべく近づけるように、flex-growflex-shrink の値を加味しながら親要素のサイズ内に収まるように各要素の横サイズを決めてくれます。

ただし、この時 flex-shrink の値が 「0」、つまり縮小を許容しませんという値になっていると、無理矢理 500px で描写するので、親要素のサイズが足りないとはみ出す (後述するマルチラインレイアウトの場合は折り返しが発生) することになります。

なお、横方向レイアウトの時は横サイズの指定になりますし、縦方向レイアウトの場合、この値は高さの指定になります。

また、flex プロパティに下記表の左側セルのようにキーワードを指定すると、それぞれ flex-growflex-shrinkflex-basis の値は右側セルのように扱われます。

同等の指定
initial flex: 0 1 auto
auto flex: 1 1 auto
none flex: 0 0 auto

マルチラインレイアウトとシングルラインレイアウト

残念ながら Firefox ではまだ実装されていませんが、Opera や IE11 では実装済み、また Google Chrome ではベンダプレフィックス付きながら使用できるプロパティとして、flex-wrap プロパティがあります。これは、Flex item のサイズが Flex container のサイズを超えたとき、折り返して 「マルチライン」 として表示するか、1行 (列) のまま 「シングルライン」 として表示するかを指定します。

なお、flex-wrap プロパティには下記の値が指定できます。

  • nowrap : 折り返ししない
  • wrap : 折り返しする
  • wrap-reverse : 折り返しするが、cross-start / cross-end (後述) を逆に扱う

ステップ 8 として、下記のように指定してみましょう。

#main {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: row;
  flex-direction: row;
  -webkit-flex-wrap: wrap;
  flex-wrap: wrap;
}
.content {
  display: -webkit-flex;
  display: flex;
  -webkit-flex-direction: column;
  flex-direction: column;
  -webkit-flex: 250px;
  flex: 250px;
}

前述の通り、Firefox では動作しませんが、Chrome などで見てもらうと、ウィンドウサイズを小さくしていって、250px の横幅で 3つの div.content が横並びになるサイズより小さくなると、一番右側の div.content が所謂カラム落ちのような感じで折り返されると思います。

で、この時 flex-growflex-shrink の値は省略されていますがそれぞれ 「1」 がセットされていますので、2カラム + 1カラム状態になった上で、横サイズが再調整されて、親要素の横サイズ内で均等にレイアウトされると思います。いい感じですね。

ちなみに、最初の方で紹介した flex-direction プロパティと、flex-wrap プロパティは、flex-flow プロパティというショートハンドプロパティでまとめることもできます。

例えば、

.content {
  flex-direction: row;
  flex-wrap: wrap;
}

という記述は、下記のようにまとめられます。

.content {
  flex-flow: row wrap;
}

ベンダプレフィックス付きの記述を加えると下記のように。

.content {
  -webkit-flex-flow: row wrap;
  flex-flow: row wrap;
}

このマルチラインレイアウトは、縦方向レイアウトの場合でも同様に機能します。下記はそのサンプル。

div#main に対する Flexible Box の指定は解除して、div.content 内の section 要素を縦方向レイアウトの Flex item として扱いますが、div.content にわざと height を指定して、高さを固定してしまいます。すると、その高さで縦に入りきらなかった Flex item は、折り返されて横に並んでいきます。

このマルチラインレイアウトは、CSS Writing Modes Module と組み合わせたりするとまたさらに複雑な感じになりますが、今回は長くなるので割愛します。

Flex Layout ボックスモデルと用語

これは最初に説明するべきなのかもしれませんが、ここで CSS Flexible Box で使われる用語について簡単に。下記の図は W3C の仕様書からの引用ですが、それぞれの用語について下記に説明を。後述する解説でこの用語が出てきますので。

flex box で使用する用語

main axis

Flex container 内のレイアウト時に基準となる軸です。

main-start / main-end

Flex item は通常、main-start から main-end に向けてレイアウトされます。縦方向レイアウトの場合は main-start が上端、 main-end が下端になります。

cross axis

main axis と垂直に交わる軸です。横方向レイアウトの場合は縦軸になりますし、縦方向レイアウトの場合は横軸が cross axis になります。

cross-start / cross-end

マルチラインレイアウトでは、cross-start 側から cross-end 側に向かって Flex item が配置されます。

覚えておかないといけないのは、上の図でいう、main axis は横軸になっていますが、これは flex-direction: row; の場合です。flex-direction: column; の場合はこの軸が縦軸になり、それと垂直に交わる横軸が cross axis になります。

つまり、後述する justify-content プロパティは、flex-direction: row; な Frex item に対しては横方向のレイアウト指定になりますが、flex-direction: column; な Frex item に対しては縦方向のレイアウト指定になります。これは、main axis の方向が変わるからです。

さて、上の方で、flex-direction: row-reverse;flex-direction: column-reverse; という指定をしましたが、これを指定すると、main-start / main-end の位置関係が逆になってレイアウトされることになります。

また、flex-wrap: wrap-reverse; が指定されると、マルチラインレイアウトの場合に、cross-start / cross-end を逆にしてレイアウトされます。

justify-content プロパティ

justify-content プロパティは main axis 方向の余白を、Flex item の周囲にどういう風に配置するかを指定します。

flex プロパティ、あるいは flex-grow プロパティで、flex-grow の値が 「0」 以外になっていると、余白は各要素のサイズの方に割り振られてしまうのでこのプロパティを指定しても意味がありません。

値としては下記が指定できます。

  • flex-start : main-start を起点に詰めてレイアウトします (初期値)
  • flex-end : main-end を起点に詰めてレイアウトします
  • center : 中央に配置します
  • space-between : 最初の Flex item は main-start に寄せ、最後の Flex item は main-end に寄せた上で、残りを均等に割り付けます
  • space-around : 全ての Flex item を均等に割り付けます

実際にどうなるかは、仕様書内の画像がわかりやすいと思いますので下記に引用します。

justify-content の値ごとのレイアウト例

align-items / align-self プロパティ

align-items プロパティは justify-content プロパティと同様、各 Flex item の配置を指定しますが、こちらは cross axis 方向に対する指定になります。値としては下記が指定できます。

  • flex-start : cross-start を起点に詰めてレイアウトします
  • flex-end : cross-end を起点に詰めてレイアウトします
  • center : 中央に配置します
  • baseline : ベースラインが一直線になるように配置
  • stretch : 同一ライン上の各 Flex item がすべて同じ高さになるように配置 (初期値)

これも実際にどうなるかは、仕様書内の画像がわかりやすいと思いますので下記に引用します。

align-items の値ごとのレイアウト例

align-self プロパティは、各 Flex item に指定することで、親要素に指定した align-items の値を個別に上書きできます。例えば align-items: center; を指定しておいて、一番最初の Flex item だけは、align-self: flex-start; で cross-start 端に寄せるといったことが可能です。

指定できる値は下記。align-items プロパティと同じですが、auto という値が加わっています。

  • auto : 親要素の align-items 値を計算 (初期値)
  • flex-start
  • flex-end
  • center
  • baseline
  • stretch

align-content プロパティ

マルチラインレイアウトの場合、各ラインの配置を指定します。align-items プロパティは Flex item が対象でしたが、こちらは各ラインが対象になります。指定できる値は下記。

  • flex-start : cross-start を起点に詰めてレイアウトします
  • flex-end : cross-end を起点に詰めてレイアウトします
  • center : 中央に配置します
  • space-between : 最初のラインは cross-start に寄せ、最後のラインは cross-end に寄せた上で、残りを均等に割り付けします
  • space-around : 全てのラインを均等に割り付けします
  • stretch : 余白を各ラインに均等に割り振って均等にレイアウトします (初期値)

これも実際にどうなるかは、仕様書内の画像がわかりやすいと思いますので下記に引用。

align-content の値ごとのレイアウト例

まとめ

さて、かなり長くなりましたが、一通り CSS Flexible Box の使い方がおわかり頂けたんじゃないかと。解説を省略している部分もありますので、細かい点は仕様書の方を見てみてください。

CSS Flexible Box をうまく使えば、フレキシブルなレイアウトが簡単に作れることができます。例えば、所謂タイル上に要素を並べたあれ、みたいのとかも簡単に実現できます。下記は実際にやってみたサンプル。

あと、下記にこの記事内で使ったサンプルページを一式まとめておきましたので、興味がある方は色々いじってみて、どんなレイアウトができるか試してみるのもいいと思います。

関連エントリー

Recent Entry

全ての記事一覧を見る

Hot Entry

逆引きおすすめエントリー