CSSから考えるデザインのディテール第2回:FlexBoxで作る柔軟な横並びメニュー

ボタンがいくつあっても横幅いっぱいに伸縮自在に均等配置する、Flexboxを使った横並びメニューの作り方を解説します。

伸縮自在な均等配置する、横並びメニュー

今回担当するのは、秋葉秀樹です。紹介するのは、ボタンがいくつあっても横幅いっぱいに伸縮自在に均等配置する、横並びメニューです。従来のfloatを使ったレイアウトでは、こうしたメニューを実現するのは難しいですが、今回紹介するFlexBoxを使えば、下図のように3個だろうが5個だろうが、横幅を均等に配置することができます。

もちろんワンソースで実現します、横幅はブラウザーが自動で解釈してくれる便利なCSSテクニックです。

FlexBoxは今のところ、スマホサイト案件で役に立つテクニックです。もちろん古いInternet Explorerが対応していないだけで、いずれはすべての環境で使えるテクニックになるはずなので、今のうちからマスターしておきましょう!

FlexBoxとは

FlexBoxは、正確には「CSS Flexible Box Layout Module」と言います。本記事では略称して「FlexBox」と記載します。

CSS Flexible Box Layout Module Level 1

文字通り、柔軟なボックスレイアウトを可能にするCSSの仕様で、非常に簡単な言い方をすると、HTML要素を思い通りにレイアウトしやすくなります。

例えば、従来の「float:left」を使った横並びレイアウトは、高さがとれないので、俗に言う「Clearfix」と呼ばれるテクニックが必要だったり、横幅がはみ出すと次の段に落ちてしまったり、色々と面倒なレイアウト手法でした。しかし、このFlexBoxではもっと便利に、そして自由になります。

現時点におけるブラウザー対応状況

気になるのは「案件で使えるのか?」というところでしょう。現時点では、ほとんどのブラウザーの最新バージョンが、このFlexBoxに対応しています。Safariでは、「-webkit-」ベンダープリフィックス付きで動作します。なお、私のAndroid 4.2のブラウザーでは、ベンダープリフィックス付きでも非対応でしたが、Android対策は記事の最後に解説します。

Internet Explorer 9が非対応なのはちょっと痛いですが、古いブラウザーを除けば商用サイトなど通常の案件でも使える可能性があります。先日のWindows XPのサポート終了によって、新しいInternet Explorerがどんどん普及することを願いたいものです。

Android 4.2以前のスマートフォンに関しては、FlexBoxの古い文法であれば対応しているので、私はスマートフォン案件でよく使っています。よって現時点では、「スマートフォン案件には使えるテクニック」として理解していただいてもよいでしょう。

FlexBoxで横並びレイアウトを実現する

それでは、基本的なHTMLとCSSを書いて、FlexBoxを試してみましょう。このFlexBox、突き詰めると非常に難しい部分もあるので、今回は簡単なところだけ押さえます。

まずは、基本コードを書きます。説明のためCSSはHTML内に記述しているのでご了承ください。

<!doctype html> <html lang=”ja”> <head> <meta charset=”UTF-8″> <meta name=”vInternet Explorerwport” content=”width=device-width; initial-scale=1.0;”> <title>Flexible Box Navigation</title> <style> /* リセット */ html, body { margin: 0; height: 100%; font-family: sans-serif; } ul { list-style-type: none; margin: 0; padding-left: 0; } li { display: block; } </style> </head> <body> <nav> <ul> <li><a href=”#”>ホーム</a></li> <li><a href=”#”>製品</a></li> <li><a href=”#”>サポート</a></li> <li><a href=”#”>会社情報</a></li> <li><a href=”#”>ご購入</a></li> </ul> </nav> </body> </html>

上記コードの表示結果が次の図です。

単純にマージンなどをリセットした状態です。ここからボタンのスタイルを決めていきましょう。スタイルの作り方は、前回「画像を使わず、CSSだけでこだわりボタンを作る」をご覧ください、その記事内容の通りにデザインしていきます。

以下のCSSコードを追加します。

/* その他スタイルの指定 */ li { text-align: center; } li:not(:last-child) { border-right: 1px solid rgba(0,255,255,0.2); } li a { display: inline-block; padding: .5em 0; text-decoration: none; width: 100%; color: #fff; text-shadow: 0 -1px 0 rgba(0,0,0,0.5); border-bottom: 2px solid rgba(0,0,0,0.2); border-top: 2px solid rgba(255,255,255,0.2); background-color: #069; }

横並びの仕切り線を付けたいので、「li:not(:last-child)」のように「最後のli要素じゃなかったら…」という否定型の条件付きセレクターを使って、最後以外のli要素には右側にボーダーを付けておきました。

その表示結果が次の図です。

display: flex

さて、横に並べましょう。基本の基本ですが、フレキシブルにするボックスには「親と子」の関係を把握しておく必要があります。例えば、今回でいうFlexBoxの影響を受けるのは「li要素」であり、その親は「ul要素」です。

次のCSSコードのように、ul要素に対して「display: flex」を指定すると、そうするだけでいきなり横並びに変化します。

ul { display: flex; display: -webkit-flex; /* Safari */ }

上記CSSコードを追加した表示結果が次の図です。

justify-contentプロパティ

justify-contentプロパティは、横方向に対しての配置方法を指定します。値は、flex-start、flex-end、center、space-between、space-aroundの5種類です。

その効果は以下のようになります。薄い赤、緑、青紫のエリアが、今回のli要素の配置位置になると思ってください。ここがfloatと違い、「均等配置」が可能なんですね。

(図の引用先:http://www.w3.org/TR/2014/WD-css-flexbox-1-20140325/#propdef-justify-content

さてここで、以下のようにCSSコードを追加しました。

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

上記CSSコードを追加した表示結果が次の図です。いろいろと横幅を変化させても、各要素が勝手に均等に配置されます。

flexプロパティ

これまでの状態は、均等配置されてはいるものの、各li要素の横幅がコンテンツの幅(今回は文字の幅)しか取ってなくて、ちょっと間が抜けた感じですね。これを解決するため、「間が抜けている空白をどう埋めるか」を指定して、ボタンの青い背景いっぱいにしましょう。

ちょっとややこしいですが、flexプロパティというものがあり、今回でいうli要素の横幅について、どうコントロールするのかを詳細に指定できます。ただし、この説明は非常に複雑になりがちなので、2つのパターンだけ紹介します。

【flex: auto】
flexプロパティの値をautoに設定すると、文字の量が反映された状態で各ボタンの余白が埋められます。

li { flex: auto; -webkit-flex: auto; }

【flex: 1】
flexプロパティの値を1にすると、それぞれのボタンの横幅がすべて同じに調整されます。

li { flex: 1; -webkit-flex: 1; }

flexプロパティのさらなる理解、flex-growプロパティから

実はこのflexプロパティに関する値の指定はもっと複雑で、3つの値「flex-grow」「flex-shrink」「flex-basis」からなるショートハンドプロパティになっています。いきなり説明するとわかりにくいので、今回はflex-growプロパティのみ簡単に試してみることにしましょう。

このあたりが理解できたら、例えば「ホームだけ他のボタンより余白1個分大きく取りたい」ということが可能です。そうすると、どれだけ横幅が可変しても、一定の大きさをキープしながら「ホーム」だけがちょっと大きめに計算されて表示されます。

先ほどのli要素に対するCSSコードから、いったんflexプロパティを消して、以下のように変更しましょう。

li { flex-grow: 1; -webkit-flex-grow: 1; /* Safari */ } li:first-child { flex-grow: 2; -webkit-flex-grow: 2; /* Safari */ }

こうすると、他の余白が1とすると、ホームだけ2となるので、次の図のような結果になります。

古いAndroidに対応させるために

Android 4.2以前の標準ブラウザーは、このFlexBoxの文法に対応していません。厳密には、古い文法で対応しているのですが、FlexBoxの文法が変わってしまったのです。そこで、Androidの標準ブラウザーには、以下のようなコCSSコードで対応することになります。

ul { display: -webkit-box; /* Android 2~4 */ -webkit-box-pack: justify; /* Android 2~4 */ } li { -webkit-box-flex: 1; /* Android 2~4 */ }

「display: -webkit-box」が「display: flex」にあたり、「-webkit-box-pack: justify」が最新仕様でいうjustify-contentプロパティにあたり、「-webkit-box-flex: 1」が最新仕様でいうflexプロパティやflex-growプロパティにあたります。

上記CSSコードを追加した表示結果が次の図です。Androidの標準ブラウザーでも均等配置が実現できました。

スマートフォンサイト、最新PCブラウザーに対応

では、これまでの流れをまとめましょう。FlexBoxを使った横並びレイアウトを、最新のInternet Exploler、Chrome、Firefox、Safari(iOS含め)、Androidの標準ブラウザー、Android Chromeで実現するコードは以下のようになります。

<!doctype html> <html lang=”ja”> <head> <meta charset=”UTF-8″> <meta name=”vInternet Explorerwport” content=”width=device-width; initial-scale=1.0;”> <title>Flexible Box Navigation</title> <style> /* リセット */ html, body { margin: 0; height: 100%; font-family: sans-serif; } ul { list-style-type: none; margin: 0; padding-left: 0; } li { display: block; } /* その他スタイルの指定 */ li { text-align: center; } li:not(:last-child) { border-right: 1px solid rgba(0,255,255,0.2); } li a { display: inline-block; padding: .5em 0; text-decoration: none; width: 100%; color: #fff; text-shadow: 0 -1px 0 rgba(0,0,0,0.5); border-bottom: 2px solid rgba(0,0,0,0.2); border-top: 2px solid rgba(255,255,255,0.2); background-color: #069; } /* 【今回重要!】Flexboxのための追加指定 */ ul { display: -webkit-box; /* Android 2~4 */ -webkit-box-pack: justify; /* Android 2~4 */ display: flex; display: -webkit-flex; /* Safari */ justify-content: space-around; -webkit-justify-content: space-around; /* Safari */ } li { -webkit-box-flex: 1; /* Android 2~4 */ flex: auto; -webkit-flex: auto; } </style> </head> <body> <nav> <ul> <li><a href=”#”>ホーム</a></li> <li><a href=”#”>製品</a></li> <li><a href=”#”>サポート</a></li> <li><a href=”#”>会社情報</a></li> <li><a href=”#”>ご購入</a></li> </ul> </nav> </body> </html>

いかがでしたでしょうか? 実は、FlexBoxには、他にもたくさんの機能があります。今回横方向だけでしたが、縦方向の中央配置や均等配置、あるいは従来のCSSだけでは難しかったボックスの水平垂直方向の真ん中に自動配置させたり、ボックスの右下に配置させたりといったことも可能です。

また機会があればもっと詳しく紹介したいと考えています。これからもどうぞよろしくお願いします!