【CSS】Flexアイテムを左右いっぱいに広げつつ、数が足りないときは左寄せにする方法

たまには実用的なテクニックも書きます。
Flexは非常に便利なプロパティですが、ちょっと凝ったレイアウトを実装しようとすると途端に難易度が上がる、というかややこしくなるんですよね…そこで今回紹介するのは、コンテナの左右までコンテンツを配置しつつ、アイテム数が足りないときだけは左寄せにする、という制御を、CSSだけで実現するテクニックです。
目次
今回作りたい物のイメージ(3カラム)
作りたいレイアウトをざっくり図で表すとこんな感じ。

.item_containerの幅いっぱいまで.itemを並べつつ2行目は左寄せするという、よくみるレイアウトです。なんかfloat使ったほうが楽なような気もしますが、floatとかもう何年も使ってないから忘れちまったよ。clearfixとか付けなきゃいけないのもめんどくさいし…
…んで、図の赤矢印は.item自体に付けたmargin-bottomで、青矢印がFlexを活用して空ける余白っていうイメージですね。
まずは普通にjustify-contentを使っただけの場合、どういう挙動になるのかを確認してみましょう。
普通にspace-betweenを使っただけの場合の挙動
HTML・CSS・表示結果
<div class="item_container"> <div class="item">1</div> <div class="item">2</div> <div class="item">3</div> <div class="item">4</div> <div class="item">5</div> </div>
.item_container{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 640px;
background: #fff;
}
.item{
margin: 0 0 20px;
width: calc((100% - 40px) / 3); /* (コンテナ幅 - (アイテム同士の余白*(カラム数-1))) / カラム数 */
height: 100px;
background: #000;
color: #fff;
}
※装飾用のプロパティは省略しています
See the Pen Flex_1 by Leo_8192 (@Leo_8192) on CodePen.
このように中のアイテム数が5個の場合、2行目の中にはコンテンツ数が2個しかないので中央にガッツリ空白が入ってしまいます。この現象が発生するのはアイテム数が2,5,8,11,14…個の場合です(3n+2)。
それでは早速対策を載せます。
対策:空の疑似要素(::after)を配置する
See the Pen Flex_1 by Leo_8192 (@Leo_8192) on CodePen.
やったことはさっきのサンプルに下記のスタイルを追加しただけです。
.item_container::after{
content: "";
width: calc((100% - 40px) / 3);
}
これだけだと何が起こっているのかイメージしにくいので、図で見てみましょう。

このように、.itemと同じ横幅を持った疑似要素を.item_containerの末尾に配置することで3n+2個のアイテム数の場合でもキレイにレイアウトを整えることが出来ます。
この方法の良いところは、生成している疑似要素には高さが無い(見た目上の影響がない)ためカラム数が変わった場合でも表示が崩れないっていう点ですね。Flexの配置を制御するためだけに置いているんで、簡単に組み込めます。
4カラムへの応用
先程のテクニックを使えば、4カラムのFlexボックスでも同じような挙動をさせることが可能です。
See the Pen Flex_2 by Leo_8192 (@Leo_8192) on CodePen.
.item_container::before{
content: "";
width: calc((100% - 40px) / 4); /* 4カラムなので4で割ってます */
order: 99999;
}
.item_container::after{
content: "";
width: calc((100% - 40px) / 4); /* 4カラムなので4で割ってます */
}
単純に::beforeを追加しただけですが、そのままだとコンテナ内の先頭に配置されてしまうのでorder:99999;にして無理矢理後ろに持っていっています。
5カラム以上は?
5カラム以上のレイアウトになると疑似要素だけでは対応できないので、ちょっとコードは汚いですがあらかじめレイアウト用の空のdiv要素をコンテナ末尾に配置しておくしかないです。別に何個配置しても見た目には影響ないので、気にならない人なら疑似要素を使わずに空のdivを配置しまくっても別に問題有りません。
そんなの嫌だ!って人には次の方法がオススメ。
space-betweenを使わない方法
実はspace-betweenを使わなくても同じようなレイアウトは作れます。
See the Pen Flex_3 by Leo_8192 (@Leo_8192) on CodePen.
パッと見はspace-betweenを使っているように見えますが、使っているのはflex-startです。
どういう挙動をしているのか図で見ると…

赤矢印が.itemにセットしたmarginですが、③のみmargin-rightがないので見た目上はjustify-content:space-between;と何も変わらない表示になります。
それでは3個目のアイテムからmargin-rightを取り除く方法を見てみましょう。
CSS
.item_container{
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
width: 640px;
background: #fff;
}
.item{
margin-right: 10px; /* アイテム同士の余白 */
margin-bottom: 20px;
width: calc((100% - 20px) / 3); /* (コンテナ幅 - (アイテム同士の余白*(カラム数-1))) / 3 */
height: 100px;
background: #000;
color: #fff;
}
.item:nth-child(3n){
margin-right: 0;
}
ポイントは16-18行目ですね。
この指定方法を使えば、3カラムのときはアイテム数が何個だろうが常に右端のアイテムを指定できます。
注意点
レスポンシブ対応したサイトだと、スマホでの閲覧時はカラム数を3→2→1みたいに変化させることが多いので、.item:nth-child(3n)という指定方法だとレイアウトが崩れます。
…なので、3→2カラムにカラム落ちする横幅にブレークポイントを設定し指定方法を.item:nth-child(2n)や.item:nth-child(even)に切り替えて余白も再調整、次に2→1カラムにカラム落ちする横幅に…というクソめんどくさい調整をする必要があります。
てか1~2カラムになったらmargin-rightを消してjustify-contentのほうを制御したほうが楽っすね。
例:5カラム超えても余裕です
さっきの方法だと適当に数値を変えれば何カラムだろうが作れます。
See the Pen Flex_4 by Leo_8192 (@Leo_8192) on CodePen.
カラム落ちを考慮すると頭が痛いですが、まぁそこはパワーで何とかしましょう。
最後に
Flexでもこういうレイアウトが作れる!というのを紹介しましたが、カラム数が多かったりもっと複雑なレイアウトを組みたいときは多分gridを使ったほうが楽です。
まだ知識不足でグリッドレイアウトの詳しい紹介はできないんですが、使いこなせるようになったらまた記事にします。



.item:nth-child(3n){ margin-right: 0; }.item:nth-child(3n)とすることで、3の倍数番目の.itemのみにスタイルを適用させています。