Flexbox、おもしろいですよ?

Translation of: Learn You a Flexbox for Great Good! | The Haystack. written by Stephen Hay

Flexboxについて知っていますか? 多分、名前は聞いたことがあるでしょう。もしかしたらチュートリアルくらいは読んだことがあるかもしれません。既に試していたりしますか?

Flexboxという代物についてあまり聞いたことがなかったり、前に試してから随分と経つなら、そのFlexboxに関する知識のことは一旦全て忘れてしまいましょう! 現時点での最新版である2011年11月29日にリリースされた仕様では完全に別物になっています。

訳注

2012年3月22日に新しく公開されたWorking Draftでもまた大きな変更が加わり、この文書の一部はそのWDにそぐわないものになっています。大筋は変わりませんし、2012年3月26日現在ではこのWDに対応した実装はないので、あまり気にする必要は今のところありません。

What you’ll need

現時点での最新の仕様を一部でも満たしているのはChrome Canaryだけです。つまり必要なものはChrome Canaryとテキスト・エディターだけということになります。

準備はいいですか? それでは始めましょう!

訳注

この日本語訳を公開した時点のChrome安定版では既にこの文書で触れるFlexboxのプロパティーは実装されています。つまり最新のChrome安定版をインストールしているならわざわざCanaryを入れる必要はありません。

What is Flexbox?

FlexboxとはCSS Flexible Box Layout Moduleのことで、CSSでレイアウトを行うために策定されている仕様の一つです。FlexboxではUI向けに最適化された柔軟なボックス・モデルが提供されます。簡単に言うと、「ボックス」を親のサイズに合わせて縦方向にも横方向にもきれいに並べることができるようになると同時に、その余白の部分の制御も行えるようになるものです。それら「ボックス」はネストすることもできるので、複雑なレイアウトもFlexboxのみで可能になります。

CAUTION!

CSSでレイアウトを行うために策定されている仕様は他にもあり、その中には僕の考えでは通常のWebページのレイアウトにおいてはFlexboxよりも向いているものがあります。Flexboxは単純明快なものですが、複雑なレイアウトを行う際には御しがたいものにもなりうるでしょう。どんなレイアウトにも対応できる仕様などはありません。ただしボタンやフォーム、ツールバーなどのUIコンポーネントやfloatを使ったカラム・レイアウトに関しては、Flexboxよってもっとシンプルに書くことが出来るので大幅な時間の節約につながるでしょう。またFlexboxは既存のCSSプロパティーとは干渉しないように設計されてもいます。そのお陰でFlexboxを使いつつ、floatpositionを使ったレイアウトを組み合わせることも可能になっています。

Three Little Boxes

面倒くさいならデモ・ページをダウンロードして、それを直接変更してください。そうでないなら……

エディターを開き、ボックスが一つだけあるHTMLドキュメントを作成してください。とりあえずdiv要素を使って作りましょう:

<!DOCTYPE html>
<html lang="en">
    <head>
         <meta charset="utf-8">
         <title>Flexbox</title>
    </head>
    <body>
        <div>
        </div>
    </body>
</html>

できましたか? わかりやすいように少しスタイルを加えましょう:

<style>
    body>div {
        height: 500px;
        padding: 1em;
        background-color: gray;
    }
</style>

次にボックスを3つdiv要素に追加します。勿論、最初に作ったdiv要素の子として作るという意味です:

<div>
    <div>A</div>
    <div>B</div>
    <div>C</div>
</div>

これらにもスタイルを追加しておきましょう:

div>div {
    width: 100px;
    height: 100px;
    background-color: pink;
}

Canaryで見てみてください。特に不思議なこともなく3つのブロック要素が縦に並んでいるはずです。とりあえずこれでFlexboxを試す土台は整いました。では実際にFlexboxを使ってみましょう。

Defining a flexbox

子である3つボックスに対してFlexboxを使いたいわけですが、そのためにはdisplayプロパティーを使うことになります。display: flexbox;親のdiv要素に対して指定し、ボックス・モデルとしてFlexboxを採用することを宣言します。もしこの親のdiv要素をインラインにしたいのならdisplay: inline-flexbox;を代わりに使ってください。

body>div {
    display: -webkit-flexbox; /* 残念ながらプリフィックスが必要です */
    display: flexbox;
    height: 500px;
    padding: 1em;
    background-color: gray;
}

これで子の3つのdiv要素はFlexboxのボックス・モデルに準じた形でレイアウトされるFlexboxアイテムになります。ただし、それらがFlexboxアイテムであるかどうかについてはいくつかルールがあることは覚えておいてください。例えばposition: absolute;をこれら3つのdiv要素のどれか一つに指定した場合、その要素はFlexboxアイテムではなくなります。詳しくは仕様のFlexboxアイテムについてのセクションを読みましょう。

それではCanaryでこのページを見てみましょう。Flexboxアイテムがあたかもinlineinline-blockを指定したような、もしくはfloatプロパティーを指定したような形に見えますよね? FlexboxではデフォルトでFlexboxアイテムを水平方向に並べてくれます。floatプロパティーを使うレイアウトとの大きな違いは、レイアウトしたい要素それ自身ではなくその親にCSSのルールを指定することです。

Source-order independence

Flexible Box Layout Moduleという名前の通り、Flexboxでは柔軟な(flexible)ボックスを作成することができるようになります。どういうことかというと、Flexboxの領域に合わせて柔軟にサイズや位置を制御できるということです。そのことについては後ほど扱いますが、その前に「ソースの記述順に束縛されない」という特にモバイル時代には重要になる特殊な概念について説明しておきたいと思います。それではどうやればFlexboxアイテムの順序を変更することができるのか試してみましょう。

body>div {
    display: -webkit-flexbox;
    display: flexbox;
    -webkit-flex-flow: row-reverse;
    flex-flow: row-reverse;
    height: 500px;
    padding: 1em;
    background-color: gray;
}

なんと! Flexboxアイテムが右から左へ並んでしまいました。順序の制御にはflex-flowプロパティー(初期値はrowです)を使います。row-reverseで逆向きに並ぶということですね。なんとなく想像が付くように縦方向にFlexboxアイテムを並べたいならcolumncolumn-reverseを使うことになりますが、現時点ではCanaryでもまだcolumn-reverseはサポートされていません。まぁとにかくcolumnは試してみてくださいね! ひと通り試し終わったならrowに戻しておいてください。

flex-flowプロパティーは他にもFlexboxアイテムの行(または列)の折り返しを制御するための値を指定することができます。wrapwrap-reverseがそのための値になり、デフォルトでは親からはみ出しても折り返されず一列(一行)で表示されるFlexboxアイテムを折り返すようになります。wrapwrap-reverseも現時点では実装されていません(何度も言っていますね……)が。ですが使えるようになったら便利そうですよね:

body>div {
    display: -webkit-flexbox;
    display: flexbox;
    -webkit-flex-flow: row wrap; /* `wrap`により複数行に渡るFlexboxが作成できる */
    flex-flow: row wrap;
    height: 500px;
    padding: 1em;
    background-color: gray;
}

MIRROR, MIRROR…

Flexboxの全てのプロパティー、順序や向き、並び順を制御するものは文字表記の方向(writing mode)に影響を受けます。我々の多くはデフォルトで上から下、左から右を利用していますが、その方向を変更した場合Flexboxによるレイアウトもまた変化します。既にrow-reverseでどうなるかは確認したことと思いますが、デフォルト(上から下、左から右)とは違う文字表示の方向、上から下、右から左ではどうなるかわかりますか? この場合Flexboxアイテムは通常は右から左へ並ぶことになり、row-reverseを質すると逆順、つまり左から右に並ぶことになります。

では試してみましょう:

body>div {
    display: -webkit-flexbox;
    display: flexbox;
    direction: rtl; /* `writing-mode`プロパティーはCanaryに実装されていないので、代わりに`direction`プロパティーを使っています。 */
    -webkit-flex-flow: row;
    flex-flow: row;
    height: 500px;
    padding: 1em;
    background-color: gray;
}

Canaryで見てみましょう。さらにrowrow-reverseに書き換えてみると……どうなりましたか?

わかりにくいかもしれませんが、Flexboxのプロパティーは全て文字表記の方向に従って論理的に決定されるということに過ぎません。大抵の場合は文字表記の方向は統一されているはずなので大きな問題が起こることはないでしょう。

Flexboxのプロパティーがどういう効果を持つか理解するには、その基準となる方向(main axis)とそれに直交する方向(cross axis)を正確に把握することが大切です。基準となる方向とはつまりFlexboxアイテムが並べられる方向のことです。言葉を変えて言うと、Flexboxアイテムが水平方向に並んでいる(flex-flow: row;)ならば、基準となる方向は水平方向ということになります。直交する方向とは基準となる方向に直角に交わる方向ということで、この例では垂直方向ということになるでしょう。flex-flow: column;として方向を変更したなら、基準となる方向が垂直方向になり、直交する方向が水平方向になります。

うまく説明するのはなかなか難しいですが、この画像が参考になるかもしれません:

When items are horizontally positioned, the main axis is horizontal.

基準となる方向と直交する方向のことは頭に入れておいてください。後ほど利用することになります。

Order! Order in the court!

他にもソースの順序に束縛されないようにするためのプロパティーとしてflex-orderプロパティーもあります。

編集中のサンプルコードでflex-flowプロパティーがrowになっていることとdirection: rtl;が削除されていることを確認してください。ちゃんとそうなっているなら、Flexboxアイテムがbody>divの左上にA-B-Cという順序で3つ並んでいることでしょう。例えばコードでの順序はこのままで、A-C-Bという順序でFlexboxアイテムを並べたいとします。そのために使うのがflex-orderプロパティーです:

div:nth-child(2) { /* IDやクラスを使ってもいいですが、こんな感じで構わない */
    -webkit-flex-order: 1;
    flex-order: 1;
}

Canaryで見てみるとA-C-Bと並んでいますよね?

flex-orderプロパティーはFlexboxアイテムを序列付きのグループに分類します。flex-orderプロパティーを指定されていないFlexboxアイテムはグループ0に分類され、ソースでの順序のままレイアウトされます。この例では二番目のFlexboxアイテム(B)をグループ1に分類しており、AとCはグループ0のまま、つまりソースでの順序のままレイアウトされることになります。グループ1は常にグループ0の後ろにレイアウトされるので、Bは最後にレイアウトされる……ということになります。B-A-Cという順にしたいならBをグループ0のままにしておき、AとCをグループ1にすれば良いでしょう:

div>div:first-child,
div>div:last-child {
    -webkit-flex-order: 1;
    flex-order: 1;
}

Flexibility

Flexboxで最も便利な機能の一つとしてフレキシブルなボックスを作ることができることが挙げられます。この例で言うところのbody>divの余った部分を利用できるということで、全てのFlexboxアイテムで均等に分割することや、Flexboxアイテムの一つで残りの領域を埋めるなどということができます。

まずは均等な幅の割り振りについて説明します。残りの領域をうまく割り振る方法についてはその後説明することにしましょう。

サンプルコードにおける3つのFlexboxアイテムがモバイル機器で使われるWebアプリケーションのボタンだと想像してみてください。3つだったり2つ(同意して次へ進むなどというようなもののみのケース)だったり……つまり「ボタンの数に係わらず、全て同じ幅で、親の幅一杯に表示したい」というようなケースです。これまではこういったレイアウトを行いたい場合JavaScriptの助けを借りてきましたが、Flexboxがあればもうその必要はありません。Flexboxアイテムでflex()関数を利用するとどうなるか見てみましょう:

div>div {
    /* width: 100px; */
    width: -webkit-flex(1 0 100px);
    width: flex(1 0 100px);
    height: 100px;
    background-color: pink;
}

flex()関数はwidthheightプロパティーの値として利用することができます。引数として拡大するかどうか、縮小するかどうか、そしてデフォルト・サイズの3つを取ることができます。3つの引数のうち最後の2つはオプションで、省略した場合は縮小するかどうかは0、デフォルト・サイズは0pxとみなされます。flex()がどう機能するかを簡単に説明すると以下のような形になります:

  1. Flexboxアイテムにはまずデフォルト・サイズが割り当てられる。
  2. 親に余っている領域があるなら、拡大するかどうかの指定に従って均等に余りが分配される。
  3. 逆にFlexboxアイテムが親をはみ出す場合は、縮小するかどうかの指定に従って均等に幅が狭められる。縮小するかどうかの指定は正の数値で指定することには注意(例: -2ではなく2)。

拡大するかどうかの指定で1を指定されると、通常は「残りの領域を均等に分けて貰う」ということになり、2を指定された場合は、「残りの領域からflex(1)の倍分けてもらう」ということになります。つまり「2で分けるとウィスキーになって、1ではコーラ」というような支離滅裂な話ではありません。どういう挙動になるのかは実際に試してみるのが手っ取り早いでしょう。div>divに続けて以下のCSSルールセットを書いてみてください:

div>div:first-child,
div>div:last-child {
    width: -webkit-flex(2);
    width: flex(2);
    background-color: magenta; /* わかりやすいように色を変えているだけで、目に悪い */
}

Canaryで見ると、最初と最後のFlexboxアイテムが余っている領域を二つに分けているわけではないことがわかりますよね?flex(1)である真ん中のボックスが確保する領域の倍確保されているはずです。もちろんあなたがブラウザーの開発者ではないことは百も承知ですが、それでも「可及的速やかに実装しろ!」とここで声を大にして言わせていただきたい。

注意すべき点としては、外側のボックスは真ん中のボックスの倍の幅にはならないということが挙げられます。つまり、残りの領域をどう分割するかということのみ制御できるという言い方もできますね。

とりあえず最後に追加したコード(flex(2)を指定したコード)はここで一旦削除してください。そしてFlexboxアイテムであるdiv要素の内一つを削除して、その結果どうなるかをCanaryで確認してみてください。ひと通りflex()の挙動を確認したら忘れずにdiv要素を戻しておきましょう。

flex()についてはバカみたいに細かく仕様でいろいろ取り決められているので、Flexboxについてより詳しく知りたいなら熟読すると良いでしょう。

Alignment of flexbox items

基準となる方向と直交する方向のことは覚えていますか? おっ、覚えてるとはすごいですね!flex-packプロパティーは基準となる方向に対してFlexboxアイテムがどう並ぶかを決定するものになります。サンプルでは基準となる方向は水平方向になっています。flex-packプロパティーがどう機能するかをきちんと把握するためにwidthプロパティーを100pxに戻しておきましょう:

div>div {
    width: 100px;
    height: 100px;
    background-color: pink;
}

flex-packプロパティーはstartendjustify、そしてcenterの4種類を値として取ることができます。わかりやすいですね。ではまずFlexboxアイテムを中央寄せしてみましょう。このプロパティーは親要素で使用します:

body>div {
    display: -webkit-flexbox;
    display: flexbox;
    -webkit-flex-flow: row;
    flex-flow: row;
    -webkit-flex-pack: center; /* <-- */
    flex-pack: center;
    height: 500px;
    padding: 1em;
    background-color: gray;
}

ついでに他の値も試してみてください。

もちろんFlexboxアイテムを直交する方向に対して並べるためのプロパティー、flex-alignプロパティーもあります。flex-packプロパティーとは値として取れるキーワードに少し違いがありますが似たようなものです。flex-packプロパティーがFlexboxアイテムの親に対して使用するプロパティーであるのに対して、flex-alignプロパティーはFlexboxアイテム自身で使用するということはきちんと覚えておいたほうが良いでしょう。

div>div {
    width: 100px;
    height: 100px;
    background-color: pink;
    -webkit-flex-align: center; /* start | end | baseline | stretch  <-- これらも試してみましょう!  */
    flex-align: center;
}

Canaryでも全ての値がサポートされているわけではありませんが、それほど遠くない将来にサポートされることでしょう。仕様が固まってくれば、実装も増えるに違いありません。ブラウザーベンダーはFlexboxにかなり注目しています。

Enough! Time to play

他にも複数行にまたがるFlexboxを制御するためのプロパティーがたくさん策定されていますが、現時点でそれらは試すことすら出来ません。うまくいけばこの「新しい」Flexboxを使い倒すための様々なプロパティーが実装されることにより、Webアプリケーションの開発に大きな変化が起きることでしょう。「レスポンシブ・デザイン」やモバイルにおいてFlexboxが役に立つ機会は相当なものでしょうし、フォーム要素のレイアウトやナビゲーションにおいてもまた然りです。

まだ実際のWebプロジェクトで利用する必要は無いですが、実際のWebプロジェクトのレイアウトにおいてFlexboxで何ができるかを調べることはできます。きっと思っているよりも早く実装されると思うので、今のうちに調べておくおくとライバル達に一歩差を付けられることでしょう。

楽しんでください!

訳について

この日本語訳はKyo Nagashimaが全て行いました。元文書がブログであることも踏まえてあまり硬くないですます調で意訳したため、一部原文とはかけ離れた文章になっているところもあります。また今後の更新で情報に違いが出る可能性を考えて、Web標準仕様へのリンクには2011-11-29版のWorking DraftのURLを使いました。他はなるべく原文ままにしたつもりですが、コード・サンプルに限りWeb標準プロパティーも併記するように大幅に追記しています。