2015.12.23 [水] そろそろFlexboxを勉強し始めてもいいんじゃないだろうか
学校は冬休みに入りました。こういった長期の休みで新しい知識や技術を習得するのも良いのではないでしょうか。

Webデザイン学科の新入生がHTML, CSSを学ぶ上で、なかなか理解しづらいのが要素を横に並べたりするときに使うfloatの扱い方です。いまのところ、Webサイトでレイアウトを組む上で、必ずと言っていいほど利用されています。ただ、親要素の高さがなくなるため、clearfixというテクニックを使ったり、幅などを計算して数値を設定しなければいけないなど、なかなかくせ者です。学生のソースを見てみると、やたらとclearfixを使ったり、positionで無理やり置いたりとあらゆる手でfloatを避けようとしていますが、避ければ避けるほどハマってしまいます。
昔からなぜ、こんなに理解しづらいものしかないのか、と思っていたんですが、そろそろCSS3 Flexible Box (可変ボックス)またはflexboxが最新のブラウザ(IEはまだ微妙ですが)で、利用ができるようになってきました。最近、Web技術系のブログでもたびたび取り上げられています。
年明けの1月12日からInternet Explorerのサポートポリシーが変更になります。これで現在サポート中のWindowsOSでのInternet Explorer11以上の利用が促されます。これから購入するPCのブラウザはEdgeになりますので、近い将来flexboxの利用が普通になってくるはずです。
ということで、flexboxの使い方を調べてみました。
Flexible Box Layoutとは
まずは、CSSの策定をしているW3Cの仕様を見てみます。どうやら最終草案のようですね。
Abstract
The specification describes a CSS box model optimized for user interface design. In the flex layout model, the children of a flex container can be laid out in any direction, and can “flex” their sizes, either growing to fill unused space or shrinking to avoid overflowing the parent. Both horizontal and vertical alignment of the children can be easily manipulated. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions.この仕様は、ユーザインタフェースの設計に最適化されたCSSボックスモデルを記述する。Flexible Box Layout Modelでは、flex containerの子は、任意の方向に配置することができ、いずれかの未使用のスペースを埋めるために拡大または親のオーバーフローを避けるために縮小し、そのサイズを「曲げる」ことができます。子供の両方の水平および垂直位置合わせを簡単に操作することができます。これらのボックスのネスティング(垂直、または垂直内側水平内側横)が2次元でのレイアウトを構築するために使用することができます。
Google翻訳のおかげでもあるのですが、相変わらずわかりづらいですね。flex containerの中で水平、垂直に「曲げる」ことができるらしいです。「曲げる」ってなんだ?実際に使って理解するほうがいいですね。
では、実際に使ってみましょう
横並びといえば、ナビゲーションですので、作ってみましょう。
floatだとCSSは下記のように書きます。
ul{ list-style-type: none; overflow: hidden; } li{ float: left; width: 25%; padding: 1em 0; text-align: center; background-color: #33CCCC; } li:nth-child(even){ background-color: #00CCFF; }
親要素の高さを付けるために、ulにoverflow:hiddenをつけます。
違いが分かりやすくするために、liのひとつを改行しておきます。
さらに、色を付けたり、整えたりすると
では、Flexboxを使います。先ほどと同じhtmlを使います。CSSからliのfloat:leftやulのoverflow:hiddenを外して、横並びにしたいliの親要素ulにdisplay:flexと入れます。
ul{ display: flex; }
横並びになりました。もちろん高さも持っていますし、さらにFlex itemの高さが揃います。並べるという行為は同じですが、ちょっと感動です。このdisplay:flexで定義された親要素をFlex container (Flex コンテナ)、子要素をFlex item (Flex アイテム)と呼びます。並び方は初期値flex-direction:rowが入っていて、横並びなるようになっています。変更して並び方を見てみましょう。
まずは、上から下へ並べます。
ul{ display: flex; flex-direction:column; }
続いて、横並びで逆方向から並べます。
ul{ display: flex; flex-direction:row-reverse; }
そして縦並びで逆方向から並べます。
ul{ display: flex; flex-direction:column-reverse; }
なんなんでしょうか!この簡単さは!中身を「並べるよ~」「横並び逆!」な感じですので、理解しやすいですね。
さらにfloatでよくあるのがカラム落ちですが、親要素の横幅に入らなかった場合は、どうなるのでしょうか。liの横幅を30%にします。4つありますので120%ではみ出しますね。
初期値はnowrapで、そのままはみ出されます。これを折り返すようにするには、ulにflex-wrap:wrapと書きます。
ul{ display: flex; flex-wrap: wrap; } li{ min-width: 30%; }
こちらも逆方向に設定ができるので、flex-wrap:wrap-reverseと書きます。さらに flex-directionとflex-wrapはショートハンドで一緒に書けますので、横並び逆で、改行も逆にしてみます。
ul{ display: flex; flex-flow: row-reverse wrap-reverse; } li{ min-width: 30%; }
追記:min-width:30%だけだとsafariでは、flex-wrapが効きませんでしたのでflex-basisを一緒に書くことで解決しました。flex-basisの解説は後の方でしています。
では、逆に小さくしてul内で移動、変更してみましょう。
親要素内で表示位置をカスタマイズ justify-content
justify-contentプロパティで変更します。justify-content:space-betweenとすると、最初の要素が左、最後の要素が右に移動して他の要素が均等に配置されます。
ul{ display: flex; justify-content:space-between; } li{ width: 15%; }
これも、とても便利ですね。space-aroundだと、最初の要素の左と最後の要素の右にも、他の要素との空きの半分の数値が入ります。もう親要素の横幅と子要素の横幅、marginを計算してpaddinやborderが…なんてのも必要ありません。
さらに左揃え(flex-start)や右揃え(flex-end)もできますが、中央揃え(center)が便利です。
ul{ display: flex; justify-content: center; } li{ width: 15%; margin: 0 10px; }
1行しか書きかえてないのに自由自在です。今はナビゲーションで検証していますが、本来はWebサイトのコンテンツをレイアウトすることが可能です。
ちょっと長くなってきましたが、今回は、この記事内に収めたいと思いますので、続けます。

垂直方向の揃え方 align-items
レイアウトの時に高さの基準を揃えるのに苦労しますよね。それもFlexboxでは解決してくれます。align-itemsを利用します。
高さをバラバラにした、要素を用意します。初期値はstretchで、Flexアイテムの高さを自動で合わせてくれます。高さをバラバラにした場合、flex-startで上または、左(※縦に並んでるとき)に揃います。flex-endで下または右(※)に揃います。下にしてみましょう。
ul{ display: flex; align-items: flex-end; }
その他、centerで縦の中央揃え、baselineでベースラインで揃います。レスポンシブWebデザインでスマートフォンの場合、flex-wrapで改行され、複数行で配置された場合は、align-contentを使い、まとめたブロックとして縦の位置を設定できまます。ul, liに高さをつけて、ulの背景に色を入れてわかりやすくしておきます。
Flexコンテナには、軸があります。主軸 (main axis) は flex アイテムのいずれに対しても平行な軸で、交差軸 (cross axis) は main axis に対して垂直な軸となります。このcross axisがアイテムよりも大きく余白ができた場合、align-contentを利用できます。初期値は、余白を各行に分割して配置します。
ul{ display: flex; flex-wrap: wrap; height: 10em; background-color:#FFFF99; } li{ min-width: 50%; height: 2em; }
flex-startで上または左(※縦に並んでいるとき)に、flex-endで下または右(※)centerで中央、space-betweenで均等に揃います。均等はjustify-contentと同じような配置ですので、space-aroundをしてみましょう。
この通り、各行の空白は均等になり、さらに一番上の上部と一番下の下部にも、他の行との空間の半分が入ります。
解説していませんでしたが、Flexboxを設定した場合、縦の位置を揃えるときによく使うvertical-alignは効きません。また、float:noneやclearをしても何も変化はありません。
さて、ここまでFlexコンテナに設定をしていきましたが、この後はFlexアイテムのプロパティを解説します。
Flexboxアイテムに設定する
まずは、orderです。これは、並び順を指定することができます。初期値は0で数値が小さい順に並びます。マイナスの数値も選べます。扱いやすいように各liにclassを設定します。
続いて、cssでFlexboxの設定をします。orderで数値を変更してみます。
ul{ display: flex; } li{ padding: 1em 0; text-align: center; background-color: #33CCCC; } li:nth-child(even){ background-color: #00CCFF; } li.flex-order1{ order: 3; } li.flex-order6{ order: -1; }
初期値が0なので、-1のNav6が一番左に移動し、3を設定したNav1が一番右に移動しました。こちらもスマートフォン表示の時に順番を変えたいときなどに利用できますね。
つづいて、flex-basisを使います。初期値はautoとなり、widthの数値のように扱います。
ul{ display: flex; } li{ flex-basis: 10%; }
Nav3だけに数値を設定すると、他のアイテムはautoになり、親要素の幅を分割するのでこうなります。
flex-growプロパティは、FlexアイテムがFlexコンテナ内に入っている場合、残りの領域を埋めるように拡大します。何も設定しない場合は、1が入ります。
ul{ display: flex; } li{ flex-basis: 10%; } li.flex-order4{ flex-grow: 5; }
Nav4に5を設定し、他の5つは何も記入しません。設定がない場合は、1になりますので、1×5=5、5×1=5となりますので、合計10に分割されます。Nav4はこれの5つ分の大きさになります。
flex-shrinkプロパティは、flex-growの逆にどこまで縮小できるかの設定です。FlexアイテムがFlexコンテナを超えている場合、超えた領域を各アイテムの数値(割合)で割り当てて縮小します。
ul{ display: flex; } li{ flex-basis: 25%; } li.flex-order2{ flex-shrink: 2; } li.flex-order6{ flex-shrink: 4; }
これが一番わかりづらいんですが、まず初期値は1です。Nav2に2、Nav6に4を設定しました。1×4=4と2×1=2, 4×1=4で合計すると10です。flex-basisが25%になっているので、ひとつのliの横幅はul幅約630pxの25%で約157.5pxになります。liは6つあるので、2つ分約315pxほどはみ出します。これを先ほどの合計数10の内どの割合で縮小するのかということになります。
設定していないliは1ですので、約31.5px、Nav2は2ですので約63pxを約157.5pxから引きますので、約94.5pxとなります。Nav6は4ですので31.5×4の約126pxを引き、約32.5pxとなります。
ちょっとわかりづらいですね。flex-growよりは使う機会が少ないかと思います。
追記(20160902):幅の計算方法でわかりやすい記事がありました。
Flexboxを使うなら知っておきたい「flexアイテム」の幅の計算方法| Rriver
また、flex-grow、flex-shrink、flex-basisはflexというショートハンドとして利用することができます。数値の数でどのプロパティに割り当てらるかが変わります。
flex: none /* 値 'none' の場合 */ flex: <'flex-grow'> /* 値が 1 つの構文、パターン 1 */ flex: <'flex-basis'> /* 値が 1 つの構文、パターン 2 */ flex: <'flex-grow'> <'flex-basis'> /* 値が 2 つの構文、パターン 1 */ flex: <'flex-grow'> <'flex-shrink'> /* 値が 2 つの構文、パターン 2 */ flex: <'flex-grow'> <'flex-shrink'> <'flex-basis'> /* 値が 3 つの構文 */
また、noneの場合は、 0 0 auto、autoの場合は、1 1 auto、nitialの場合は、0 1 auto と同じになります。
align-selfは、個別のFlexアイテムの垂直方向の揃えを設定します。
ul{ display: flex; height: 10em; } li{ height: 4em; } li.flex-order2{ align-self: flex-end; } li.flex-order6{ align-self: center; }
これで、Flexbox関連のプロパティの基本設定と、どんなことができるのかがわかりました。これらを組み合わせると、Pintarestのような、Masonryレイアウトもそれほど多くない行数で実現することができます。
参考:Flexboxを使った2カラム・3カラム・マルチレイアウトの基本と応用 | Webクリエイターボックス
Internet Explorer11でも、今回紹介したプロパティは、Chromeなどと同じように表示されていますので、徐々に使っていってもいいのではないでしょうか。とはいえ、まだまだfloatを使ったレイアウトは現役ですので、学校でも最初はfloatを勉強していくことになると思います。
お疲れ様でした。

参考:CSS3のFlexboxを基本から理解して、使い倒そう! | 株式会社LIG
参考:Flexboxについて勉強したい・理解を深めたい時に参考になるエントリーやサイトまとめ | NxWorld