CSSフレックスボックス(display:flex)の使い方

HTMLのレイアウト方法で最も新しいフレキシブルボックスをまめわざでも利用しています。
手軽に並列レイアウトができるスタイルですが、他の並列レイアウトとの使い分けはどうすべきか、flexにしかできない表現はあるのかを、実例を挙げながら解説します。

フレックスボックス(フレキシブルボックス)とはなにか?

ある要素に定義するだけで、その直下の要素が並列になる便利なスタイルです。
シンプルな導入であれば、CSSで「display:flex」というスタイルを指定するだけです。

HTMLの例

下記例に共通

<ul>
    <li>1番目の要素</li>
    <li>2番目の要素</li>
    <li>3番目の要素</li>
</ul> 

CSSの例

ul {
    display: -webkit-flex;
    display: flex;
} 

実例1-1

シンプルな並列

<style type="text/css" scoped>
ul {
    display: -webkit-flex;
    display: flex;
}
</style> 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

実例1-2

シンプルな並列(中央寄せ)

<style type="text/css" scoped>
ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: center;
    justify-content: center;
    -webkit-align-items: center;
    align-items: center;
    height: 200px;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

中央寄せは、並列の場合の左右方向は
justify-content: center;
で、上下方向は
align-items: center;
で指定します。

実例1-3

右下寄せ

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-end;
    justify-content: flex-end;
    -webkit-align-items: flex-end;
    align-items: flex-end;
    height: 200px;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

上の例と同じプロパティを利用します。
left/topはflex-start、right/bottomはflex-endを使う点に注意しましょう。
このような右下寄せは、他の方法では容易ではないので、利用シーンがあればflexの出番と言えます。

css flexの使いどころ

css flex以外の並列レイアウト法

使い方解説
float:left法並列させたい要素のスタイルにfloat:left要素を回り込みさせることで並列レイアウトを実現
display:inline-block法並列させたい要素のスタイルにdisplay:inline-block要素をインライン化(左寄せの場合に左詰め)することで並列レイアウトを実現
table法tableタグを利用するか
CSSのdisplay:tableを使って要素をテーブル化
要素をテーブル化することで、並列・縦列を操る

このように、並列レイアウトにはいくつか選択肢があり、必ずしもflexを採用する必要がありません。
しかし、flexにしかできない(あるいは別の方法ではコードが極めて冗長的になる)ケースがあります。これまでに利用した例から、flexの出番といえる使い方を以下に挙げてみました。

css flexならできる順番入替え

flex-directionプロパティを使うと、HTMLはそのままで、要素の順番逆転が可能です。更にrow/columnを指定して縦列でも利用が可能です。
また、orderプロパティで順番を明示できます。以下に、並列・縦列の順番入替えの例を挙げます。

実例2-1

並列の順番逆転

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-end;
    justify-content: flex-end;
    -webkit-flex-direction: row-reverse;
    flex-direction: row-reverse;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

左右中央揃えを決める
justify-content
プロパティは、デフォルトではflex-startで、要素は先頭から配置されます。
flex-direction: row-reverse
として順番を入れ替えた場合は、先頭が右側と認識されるため、左寄せにするためには明示的に
justify-content: flex-end
として末尾から配置する必要があります。

実例2-2

縦列で順番逆転

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-align-items: flex-start;
    align-items: flex-start;
    -webkit-flex-direction: column-reverse;
    flex-direction: column-reverse;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

並び順と垂直の方向(並列の場合は縦方向、縦列の場合は横方向)の左右中央揃えを決める
align-items
は、デフォルトでstretch(引延し)のため、display:blockのように隙間なく要素が伸長します。明示的に
align-items: flex-start
とすると、stretchが解除されます。

実例2-3

順番指定

ul.flx13 {
     display: -webkit-flex;
    display: flex;
}
ul.flx13 li:nth-of-type(3) {
     -webkit-order: -1;
    order: -1;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

3番めの子要素にorderプロパティで順番を指定しています。
orderのデフォルト値は0のため、負数を指定することで1番目に設置できます。
3番めに1、1番目に2、2番めに3と全要素に順番を明示することも可能です。

css flexならできる均等配置

左右中央揃えを設定する
justify-content
は、より正確に言えば「要素の展開方向(並列の場合は横方向、縦列の場合は縦方向)の要素の配置方法」を設定するプロパティです。
text-align: left/center/right(または縦列ならばvertical-align: top/center/bottom)
に対応した
justify-content: flex-start/center/flex-end
としての利用のほか、
justify-content: space-between/space-around
として均等配置を指定できます。これらの違いは、先頭の末尾の要素の前後にスペースが含まれるかどうかです。以下の実例を見れば一目瞭然です。

実例3-1

並列で均等配置(左右隙間なし=space-between)

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: space-between;
    justify-content: space-between;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

実例3-2

並列で均等配置(左右隙間あり=space-around)

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: space-around;
    justify-content: space-around;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

実例3-3

縦列で均等配置(上下隙間なし=space-between)

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: space-between;
    justify-content: space-between;
    -webkit-align-items: flex-start;
    align-items: flex-start;
    -webkit-flex-direction: column;
    flex-direction: column;
    height: 200px;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

実例3-4

縦列で均等配置(上下隙間あり=space-around)

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: space-around;
    justify-content: space-around;
    -webkit-align-items: flex-start;
    align-items: flex-start;
    -webkit-flex-direction: column;
    flex-direction: column;
    height: 200px;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

css flexならできる自在な配置

フレックスボックスの子要素にmargin: autoを設定することで、一部の要素だけを左右または上下に寄せることが可能です。以下、実例をご覧ください。

実例4-1

最後の要素だけ右寄せ

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
}
ul li:last-of-type {
    margin-left: auto;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

最後の要素にmargin-left:autoを設定し、左マージンを最大にしています。

実例4-2

2・3番目の要素だけ右寄せ

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
}
ul li:nth-of-type(2) {
    margin-left: auto;
    background-color: #ffc;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

2番めの要素にmargin-left:autoを設定し、右寄せをして、その右側にある要素も影響を受けています。

実例4-3

縦列で最後の要素だけ下寄せ

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
    -webkit-align-items: flex-start;
    align-items: flex-start;
    -webkit-flex-direction: column;
    flex-direction: column;
    height: 200px;
}
ul li:last-of-type(3) {
    margin-top: auto;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

実例4-4

組み合わせてみる

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
    -webkit-align-items: flex-start;
    align-items: flex-start;
    -webkit-flex-direction: column;
    flex-direction: column;
    height: 200px;
}
ul li:nth-of-type(1) {
    margin: 0 auto auto 0;
}
ul li:nth-of-type(2) {
    margin: auto;
}
ul li:nth-of-type(3) {
    margin: auto 0 0 auto;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

このようにmargin:autoの組み合わせによって、かなり自在な配置が可能です。

css flexならできる並列幅の指定

子要素に、下記のようなプロパティを設定することで、大きさを微調整できます。

flex-grow伸長率
(ただし%ではなく数字)
flex-shrink縮小率
(ただし%ではなく数字)
flex-basis固定サイズ
(ただし要素が指定サイズを超えてもはみ出さない)

子要素の大きさを微調整できるのは、flexの最大の特徴の1つと言えます。
多くの場合、この仕様こそが「css flexを採用せざるを得ない」一番の理由になると思われます。

実例5-1

均等に隙間を埋める

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
}
ul li {
    -webkit-flex-grow: 1;
    flex-grow: 1;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

全ての要素にflex-grow:1を指定することで、1:1:1の比率で要素が伸長し、隙間が埋まります。

実例5-2

ある要素を引き伸ばして隙間を埋める

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
}
ul li:nth-of-type(2) {
    -webkit-flex-grow: 1;
    flex-grow: 1;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

2番めの要素だけにflex-grow:1を指定することで、2番めだけが伸長し、隙間が埋まります。
1番目と3番めにflex-shrink:1を指定した場合も同様です。

実例5-3

ある要素を固定して隙間を埋める

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
}
ul li:nth-of-type(1) {
    -webkit-flex-basis: 50px;
    flex-basis: 50px;
}
ul li:nth-of-type(2) {
    -webkit-flex-grow: 1;
    flex-grow: 1;
}
ul li:nth-of-type(3) {
    -webkit-flex-grow: 1;
    flex-grow: 1;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

1番めの要素にflex-basis:50pxを指定してサイズを固定し、2・3番目にflex-growを指定することで隙間を埋めています。
2・3番目のflex-growを指定しない場合は、1番目のサイズが固定されるだけで、隙間が埋まりません。
このflex-basisは、指定したサイズにできない場合は、それ以下のサイズに調整される「緩いサイズ指定」とも呼べる大きな特徴があります。これについては、下記のフォームの例で説明します。

「flex:1」はやめたほうが良い

ネット上の解説などを見ると、隙間を埋める魔法の言葉としてflex:1が多く紹介されていますが、やりたいことはflex-grow:1であることが分かります。
flex:1は、flex-grow:1、flex-shrink:1と解釈され、flex-grow:1だけの場合とは異なる挙動を表す場合があります。
例えばIE11では、flex:1とした要素の下に、サイズを指定した要素がある場合にはみ出してしまう現象が見られます(flex-wrap:wrapを無視してnowrapになります)ので、利用しないほうが無難です。

css flexならできる並列・縦列・隙間埋めの合わせ技

flexの改行可否を指定するプロパティ
flex-wrap
は、デフォルトではnowrapで改行を禁止しています。
これをwrap(改行可)にした場合に、flexの個性が光ります。以下に例を挙げます。

実例6

並列・縦列の混合で、ストレッチ

子要素を50%とサイズ指定しました。
これにより、1番目と2番めは均等に配置されます。
一方で、3番めは改行され、flex-growを優先して(サイズ指定は無視して)伸長します。これにより、最小サイズを保ちながら隙間を埋めることが可能になります。

ul {
    display: -webkit-flex;
    display: flex;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
    -webkit-flex-wrap: wrap;
    flex-wrap: wrap;
}
ul li {
     -webkit-flex-grow: 1;
    flex-grow: 1;
    width: 50%;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
} 

  • 1番目の要素
  • 2番目の要素
  • 3番目の要素

固定幅と可変長が混合したフォームを作る

メディアクエリに頼らずフレキシブルボックスを使ってレスポンシブ・ウェブ・デザイン

上の実例を組み合わせて、次のような条件を満たすフォームを作成してみます。

  • 入力欄のサイズは可変とし、最小サイズを定める(210px)
  • 見出しの幅を統一する(120px)
  • 横幅に応じて縦列に切り替える
  • 最小サイズ以下の場合もはみ出さない

HTML・CSS

liにflexを設定し、その子要素のspanを並列配置しています。
flex-wrap: wrapとして並列と縦列を混合し、flex-basisで「はみ出さないサイズ指定」をしています。
サイズ指定は全てflex-basisに譲り、inputとtextareaのサイズは100%としています。

<style type="text/css" scoped>
ul li {
    display: -webkit-flex;
    display: flex;
    -webkit-flex-wrap: wrap;
    flex-wrap: wrap;
    -webkit-align-items: center;
    align-items: center;
}

    ul span:first-of-type {
        -webkit-flex-basis: 120px;
        flex-basis: 120px;
    }
    ul span:nth-of-type(2) {
        -webkit-flex-basis: 210px;
        flex-basis: 210px;
        -webkit-flex-grow: 1;
        flex-grow: 1;
    }

ul input[type="text"], ul textarea {
    width: 100%;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}
</style> 

<ul style="width:○○px">
<li><span>お名前</span>
<span><input type="text" name="name" size="20" /></span></li>

<li><span>電話番号</span>
<span><input type="text" name="tel" size="20" /></span></li>

<li><span>お問合せ内容</span>
<span><textarea name="content" cols="30" rows="5"></textarea></span></li>

<li><span>送信</span>
<span><input type="submit" value="送信" /></span></li>
</ul> 

出力結果

  • お名前
  • 電話番号
  • お問合せ内容(必須項目)
  • 送信

  • お名前
  • 電話番号
  • お問合せ内容(必須項目)
  • 送信

  • お名前
  • 電話番号
  • お問合せ内容(必須項目)
  • 送信

  • お名前
  • 電話番号
  • お問合せ内容(必須項目)
  • 送信

  • お名前
  • 電話番号
  • お問合せ内容(必須項目)
  • 送信

上から順に800px、500px、300px、200px、100pxとしています。
800pxと500pxでは全ての入力欄と見出しの組み合わせが並列になり、入力欄のサイズ幅だけが変動しています。
300pxでは、見出しと入力欄の推奨サイズである120+210=330pxを下回るため縦列化し、かつinputは横いっぱいに配置されます。
200px、100pxでは、それぞれ入力欄と見出しの推奨サイズを下回っているものの、はみ出すことなく表示されます。

テーブルレイアウト+レスポンシブ・ウェブ・デザイン(RWD)型との比較

このフォームは、テーブルレイアウトとRWDの組み合わせでも実現が可能ですが、並列と縦列を切り替えるためにメディアクエリを使って複数種類のCSSを定義する必要があります。
このコードではメディアクエリを使わずにRWDを実現しており、ディスプレイサイズではなく実際のフォームのサイズによってレイアウトが変わる点でメディアクエリより優れています。コーディングを減らせるメリットも有ります。

フレキシブルボックスのクロスブラウザ対策

上記のフレックスボックスが対応していないブラウザについて、今回取り上げた実例で使ったプロパティの対応表を用意しました。

旧webkit系ブラウザ

主に古いAndroid、iOS、OSXのSafariが対象です。

プロパティの効果モダンブラウザ旧webkit系ブラウザ
css flexの宣言display: -webkit-flex;
display: flex;
display: -webkit-box;
揃え並列時の左右中央揃え、
縦列時の上下中央揃え
-webkit-justify-content: flex-start (center/flex-end);
justify-content: flex-start (center/flex-end);
-webkit-box-pack: start (/center/end);
並列時の上下中央揃え、
縦列時の左右中央揃え
-webkit-align-items: flex-start (/center/flex-end);
align-items: flex-start (/center/flex-end);
-webkit-box-align: start (/center/end);
順番入替順番逆転(並列)-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
-webkit-box-direction: reverse;
順番逆転(縦列)-webkit-flex-direction: column-reverse;
flex-direction: column-reverse;
-webkit-box-direction: reverse;
-webkit-box-orient: vertical;
順番指定ある子要素に
-webkit-order: -1;
order: -1;
それぞれの子要素に
-webkit-box-ordinal-group: 1;
-webkit-box-ordinal-group: 2;
-webkit-box-ordinal-group: 3;
均等配置左右隙間あり-webkit-justify-content: space-between;
justify-content: space-between;
-webkit-box-pack: justify;
左右隙間なし-webkit-justify-content: space-around;
justify-content: space-around;
--
自在に配置margin-○○: auto--
並列幅の指定均等または一部
(一部の場合は1つの要素に適用)
子要素に
-webkit-flex-grow: 1;
flex-grow: 1;
子要素に
-webkit-box-flex: 1;
固定サイズある子要素に
-webkit-flex-basis: 50px;
flex-basis: 50px;
--
並列・縦列混合-webkit-flex-wrap: wrap;
flex-wrap: wrap;
--

並列・縦列の混合ができず、display:-webkit-boxを指定した時点で、テーブルレイアウトの如く全ての並列化します。よって、フレックスボックスの最大の利点が活かせません。残念なことに、対象の多くが古いスマートフォンのため、主にスマホ向けに利用したい「並列・縦列混合レイアウト」を利用する際にネックになります。
この場合は「display:-webkit-boxを利用しない」という選択肢を考慮しましょう。上のフォームの例では、全てが縦列になり、ユーザー体験を損ないません。全てが並列になるぐらいなら縦列の方が良い場合が多いと思われます。
また「自在に配置」ができないので、配置が極めて重要な表現を担う場合は、専用のフォローが必要です。

IE10

プロパティの効果モダンブラウザ旧webkit系ブラウザ
css flexの宣言display: -webkit-flex;
display: flex;
display: -ms-flexbox;
揃え並列時の左右中央揃え、
縦列時の上下中央揃え
-webkit-justify-content: flex-start (center/flex-end);
justify-content: flex-start (center/flex-end);
-ms-flex-pack: start (/center/end);
並列時の上下中央揃え、
縦列時の左右中央揃え
-webkit-align-items: flex-start (/center/flex-end);
align-items: flex-start (/center/flex-end);
-ms-flex-align: start (/center/end);
順番入替順番逆転(並列)-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
-ms-flex-direction: row-reverse;
順番逆転(縦列)-webkit-flex-direction: column-reverse;
flex-direction: column-reverse;
-ms-flex-direction: column-reverse;
順番指定ある子要素に
-webkit-order: -1;
order: -1;
ある子要素に
-ms-flex-order: -1;
均等配置左右隙間あり-webkit-justify-content: space-between;
justify-content: space-between;
-ms-flex-pack: justify;
左右隙間なし-webkit-justify-content: space-around;
justify-content: space-around;
-ms-flex-pack: distribute;
自在に配置margin-○○: automargin-○○: auto
並列幅の指定均等または一部
(一部の場合は1つの要素に適用)
子要素に
-webkit-flex-grow: 1;
flex-grow: 1;
子要素に
-ms-flex: 1 0 auto;
固定サイズある子要素に
-webkit-flex-basis: 50px;
flex-basis: 50px;
ある子要素に
-ms-flex: 0 0 50px;
並列・縦列混合-webkit-flex-wrap: wrap;
flex-wrap: wrap;
-ms-flex-wrap: wrap;

-ms-flex-wrap:wrap;があるものの、サイズ指定より並列が優先されるため、意図した並列・縦列混合ができません。上の旧webkitの例と同じく「全てが並列になるぐらいなら全てが縦列の方が良い」場合はdisplay:-ms-flexbox;の利用を止めましょう。ただしIE10が対象であることを考えると、常に並列でも問題がないケースが多いと思われますので、旧webkitの場合と異なり利用する機会が増えそうです。
並列幅の指定時に-ms-flexを設定する際は、必ず-ms-flex:1 0 auto;と3つの値を設定しましょう。-ms-flex:1;とした場合、IE11は2番めの値であるflex-shrinkに1が省略されていると勝手に判定し、思い通りの幅にならない場合があります。IE10対策がIE11に悪影響を及ぼすケースなので注意が必要です。
これ以外は、ほぼ全てが対応しているので、コーディングの手間以外に追記するデメリットはありません。

その他のブラウザへの配慮

並列表示にflexを利用する場合は、子要素にdisplay:inline-blockやfloat:leftを指定することで、未対応のブラウザでもある程度並列表現を再現できます。
順番入替・配置は対処のしようがないため、未対応ブラウザを無視するか、Javascriptなどで別途対応が必要です。
隙間を埋めるため、または幅を指定するために利用する場合は、他ブラウザと見た目が異なりますが、内容が表示されれば最低限のクロスブラウザは実現できます。

2016/6/27