よこ並びのCSS。
フレキシブルボックスレイアウトでよこ並び

floatプロパティは要素を左右に寄せるためのプロパティだし、displayプロパティのtableは、テーブル(ひょう)をレイアウトするための値だし、inline-blockはあくまでブロック要素だし、どれも特別にナビゲーションをよこ並びするためのプロパティというわけではありませんでした。
けれどもフレキシブルボックスレイアウトは、指定した要素の大きさを柔軟に伸縮させ、画面サイズに対して最適な大きさで表示させるために用意された値で、まさにこのナビゲーションのために用意された値なんですよね!要素を柔軟にレイアウトするための仕様なんです。

フレキシブルボックスの特徴

フレキシブルとは、“柔軟性がある様子”のこと。具体的には、displayプロパティでflexという値を指定することで、その要素でフレキシブルボックスが利用可能になります。

ちょっとだけtable/table-cellの仕組みと似てて、display: flexと指定した要素自体がよこ並びになるわけではなくて、flexと指定した要素の直接の子要素が、よこ並びされる対象となります。ただ、table-cellみたく、特別にCSSを指定する必要はなくて、親要素にdisplay: flexと指定した子要素は勝手によこ並びになるので、ちょっと不思議な感じですよね。

ul {
	list-style: none;
	display: flex;
	margin: 0;
	padding: 0;
	text-align: center;
	background: #fafafa;
}

さぁ、もうすでに上のli要素はすべてフレキシブルな要素となっています。フレキシブルボックスレイアウト用に用意された様々なプロパティを使うことで、思い通りのレイアウトが可能となります:D

display: tableと指定することで、div要素が突如table要素さながらとなったように、display: flexと指定した要素は、唐突にフレキシブルな要素に様変わりします。
テーブルはつまりひょうなレイアウトなのでわかりやすかったですが、フレキシブルボックスはいままで出会ったことのないレイアウト方法なので、なかなか慣れないですけれど、大きく以下のような特徴があります。

displayプロパティでflexと指定した要素は、「コンテナ」と呼ばれるようになります。
そして、コンテナの直接の子要素はすべて「アイテム」と呼ばれ、フレキシブルボックスレイアウト関連のプロパティの初期値によって、すべてよこ並びでレイアウトされます。
要素が並ぶ進行方向となる軸を「主軸」、それに直角な方向の軸を「交差軸」と呼びます。

直接の子要素という点もポイントで、孫要素はフレキシブルにはならないということ

フレキシブルボックスについて、詳しくは下記ページを参照のこと。
CSS flexible box の利用 - CSS: カスケーディングスタイルシート | MDN

ページトップへ戻る

ナビゲーションを中央に配置する

要素を中央に配置するには、justify-contentプロパティを使います。このプロパティは、アイテムの配置の仕方を調整するためのプロパティになります。ここではcenterという値を指定しています。

ul {
	list-style: none;
	display: flex;
	justify-content: center;
	⋮

ページトップへ戻る

ボタンのサイズを揃える

アイテムの横幅を制限するにはflex-basisプロパティを使います。下のサンプルでは、10emに指定しています。文字数によって改行したボタンの大きさが揃わないのは、フロートやテーブルの時と同じですね…。

li {
	flex-basis: 10em;
}

縦方向の位置を制御するには、テーブルやインライン・ブロックの時にはvertical-alignプロパティを使いましたが、フレキシブルボックスではフレキシブルボックス専用のalign-itemsプロパティを使います。ここでは中央に揃えるためalign-items: centerを指定しています。middleじゃないので注意です。
これまたテーブルやインライン・ブロックの時と同様、字面的には中央に揃えど、ボタンのサイズは揃わぬままです…。

ul {
	list-style: none;
	display: flex;
	justify-content: center;
	align-items: center;
	margin: 0;
	padding: 0;
	text-align: center;
	background: #fafafa;
}

そこで、li要素だけじゃなく、a要素と、さらにその中のテキストもフレキシブルに扱えるように、li要素とa要素にもdisplay: flexを指定しちゃいます!つまり、li要素もa要素も、アイテムでありコンテナになるということ。
さらに、justify-contentプロパティも同じくli要素、a要素にも指定して、flex-basisプロパティも、li要素、a要素に、同じく10emを指定します。
ここまでの指定で、ボタンの大きさが揃いました。align-itemsプロパティは、初期値としてstretchという値が指定されるため、特に何も指定しなければ、アイテムは交差軸(縦方向)の端から端まで埋まった状態になるんですね:D

ul {
	list-style: none;
	margin: 0;
	padding: 0;
	text-align: center;
	background: #fafafa;
}
ul, li, li a {
	display: flex;
	justify-content: center;
}
li, li a {
	flex-basis: 10em;
}
ぜんぶ、アイテムでありコンテナ

a要素にdisplay: flexを指定したことで、a要素の子要素がアイテムになるわけですが、a要素には子要素がないですよね…。そんな場合には、その中のテキストが、無名の要素ってことで、アイテムとして扱われることになっています:D
a要素にalign-itemsプロパティでcenterと指定すれば、そのアイテムとなるテキストが上下中央に配置されるはずです。

li a {
	cursor: pointer;
	align-items: center;
	padding: 1em;
}
要素がない時はテキストがアイテムとして扱われる

ページトップへ戻る

横幅いっぱいに広げる

横幅いっぱいに広げるには、アイテムに対して、flex-growプロパティ1以上の数値を指定します。
ここで指定した数値は、コンテナ内の余った領域を、各アイテムにどう分配するかの割合を表します。ここではアイテム全てに対して1を指定しているので、アイテムそれぞれに同じ分ずつ余白が分配されることになります。
きちんと分配しているので、table-cellよりも、アイテムのバランスがよいのがわかりますね。

li, li a {
	flex-grow: 1;
}
詰めて並べた時に余ってる領域(横幅)を、各アイテムに分配している

レスポンシブにする

そして、スマホではたて並びになるようにしましょう。テーブルやインライン・ブロックの時と同じように、フレキシブルにしてる要素をdisplay: blockに指定するでもよいんですが、せっかくフレキシブルなので、flex-directionプロパティを使って並びの向きを変えることにします。
ul要素にflex-direction: columnと指定すれば、たて並びになります。あと、パディングの向きも変えたらできあがり。

@media screen and (max-width: 767px) {
	ul {
		flex-direction: column;
	}
	li:nth-child(n+2) {
		padding: 1px 0 0;
	}
}

ページトップへ戻る

フレキシブルボックスで商品一覧

ul要素に、displayプロパティでflexとすれば、ul要素がコンテナ、li要素がアイテムとして、フレキシブルボックス関連のプロパティで扱えるようになるんでしたね。

.itemlist {
	list-style: none;
	display: flex;
	max-width: 1024px;
	margin: 0 auto;
	padding: 0;
}

早速、li要素が横並びになりましたが、全部一列で並んじゃいましたー!ちゃんと均等に並んでるからテーブルよりは綺麗だけれども…:(

※つまり、フレキシブルボックスのアイテムにはwidthプロパティは無効ということ…。

アイテムを改行させるには、専用のflex-wrapプロパティを使って、改行する旨を指定せねばなりません。初期値がnowrapで、改行しないことになってるんです。flex-wrap: wrapと指定すれば、うまく改行するようになりました。

.itemlist {
	list-style: none;
	display: flex;
	flex-wrap: wrap;
	⋮

レスポンシブにする

あとはレスポンシブに列を切り替える指定ですが、フレキシブルボックスならではの、便利な指定方法があるので、そちらを使ってみます!
指定するスタイルはひとつ、flexプロパティだけ。displayプロパティの値のflexとはまた別の、「flex」というプロパティ名になります。
flexプロパティは、アイテムの伸縮率を決めるためのプロパティで、実はこのプロパティ、ひとつめの値がflex-growプロパティ、ふたつめの値がflex-shrinkプロパティ、みっつめがflex-basisプロパティを指定することになっていて、3つのプロパティをまとめて指定するためのショートハンドプロパティになってるんです。それぞれの値を、半角スペースで区切って指定します。

.itemlist li {
	flex: 1 1 300px;
	padding: 10px;
	box-sizing: border-box;
}

ここでは、1 1 300pxと指定しました。コンテナの余った領域を均等に割り振って、アイテムの横幅を300pxに制限するってことです。下のサンプルの画面サイズを縮めていくと、アイテムの横幅が300px以下になるタイミングで勝手に改行するのがわかります。

ただ、ちょっと改行するのが早いかなーとも感じますね…。1列になった時の商品が大きすぎるので、flex-basisプロパティの値をもっと少し小さく、200pxにしてみます。これで、1列になる時のアイテムの横幅は、399pxになるはずです。

.itemlist li {
	flex: 1 1 200px;
	padding: 10px;
	box-sizing: border-box;
}
画面幅が400px未満になった時、アイテムの横幅が200px未満となるので、1列になる

横幅が狭い時は良い感じなんですけれど、200pxまで小さくなれるようになったため、横幅が1000pxの時には5つ並べるってことになってしまったんですね…!しかも最後のアイテムはコンテナの余った領域を独り占めしちゃって大変なことになってます…X(
そこで、アイテムにはmin-widthプロパティ33.333333%と指定して、3分の1以下にはならないように指定します。

.itemlist li {
	flex: 1 1 200px;
	min-width: 33.333333%;
	padding: 10px;
	box-sizing: border-box;
}

うむ、落ち着いたかな…。

しかし!7つめの商品を追加した途端、やっぱり最後の商品だけ大変なことに…。
3分の1以下にならないようにするだけじゃなくて、3分の1以上にもならないようにしなきゃダメか…。

.itemlist li {
	flex: 1 1 200px;
	min-width: 33.333333%;
	max-width: 33.333333%;
	padding: 10px;
	box-sizing: border-box;
}

いや、3分の1以上にならないということはずっと3分割ということだから意味ないですね…X(

ということで結局、以下のように指定することに…。
flexプロパティはやめて、flex-basisの値だけ33.333333%に指定しました。もう拡張はしません。そして、メディアクエリを使って、flex-basisプロパティの値を、横幅が767px以下の時には50%、414px以下の時には100%と切り替えることで、落ち着きました:Dあまりフレキシブルの良いところを活かせてないかも…。

.itemlist li {
	flex-basis: 33.333333%;
	padding: 10px;
	box-sizing: border-box;
}
@media screen and (max-width: 767px) {
	.itemlist li {
		flex-basis: 50%;
	}
}
@media screen and (max-width: 414px) {
	.itemlist li {
		flex-basis: 100%;
	}
}

ページトップへ戻る

IE11にも対応する

…と思いきや、IE11では下図のように、レイアウトが崩れちゃってました…!
横幅が広くても2列で改行しちゃってます。Can I use...にも大量のバグが存在するとありますね!

li要素に指定している、box-sizing: border-boxが効いてないみたいです…X(
ここの指定を、content-boxにすると、flex-basisの値も修正しないといけないですけれど、IE11でもちゃんと表示されるようになります。

.itemlist li {
	flex-basis: calc(33.333333% - 20px);
	padding: 10px;
	box-sizing: content-box;
}
@media screen and (max-width: 767px) {
	.itemlist li {
		flex-basis: calc(50% - 20px);
	}
}
@media screen and (max-width: 414px) {
	.itemlist li {
		flex-basis: calc(100% - 20px);
	}
}

border-boxのままにしときたい場合は、横幅をmax-widthプロパティで指定すれば、border-boxがちゃんと反映されるんですけれど、そうとなるともう、flex-basisプロパティの指定はいらなくなりますね…¦(

.itemlist li {
	max-width: 33.333333%;
	padding: 10px;
	box-sizing: border-box;
}
@media screen and (max-width: 767px) {
	.itemlist li {
		max-width: 50%;
	}
}
@media screen and (max-width: 414px) {
	.itemlist li {
		max-width: none;
	}
}

ページトップへ戻る

おわりに。

ナビゲーションと、商品一覧というシチュエーションをいろんな方法でよこ並びしてきましたが、どの方法も一長一短だなぁと感じました。長らくフロートでよこ並びしてきたこともあって、フロートの方が馴染みがあるのだけれど、やはりフレキシブルボックスの方が柔軟性はある感じ。けど今の所は「便利なテーブル」くらいの感覚かな…¦(。もっと小慣れてきたらもっと便利に感じるんだと思います;D。それに、フレキシブルボックスレイアウトはよこ並びだけじゃないですものね!

他にもこんな使い方があるよ!とか、フレキシブルボックスとはそもそも…などありましたら、ぜひ教えてくださいー。また、記事の中で、間違った使い方や、間違った説明してるところがあったらどしどしご指摘いただけると嬉しいです!

以上、「よこ並びのCSS。」でした!最後まで読んでいただきありがとうございますXD

引用・参考文献

Comment & Pingback

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください