実務から学ぶFlexbox 前編 マルチカラムレイアウトの実例

実案件でも使われることが多くなったFlexboxについて、業務で使用した実例を元に解説します。今回は、まずFlexboxを使う際に気をつけたいことを解説し、さらにマルチカラムレイアウトの実装を紹介します。

発行

著者 坂巻 翔大郎 フロントエンド・エンジニア
実務から学ぶFlexbox シリーズの記事一覧

はじめに

実際の案件でCSS Flexible Box Layout Module(以下、Flexbox)を扱うことが多くなり、筆者の経験値は「そこが知りたい、Flexible Box」のシリーズを書いていた頃よりも溜まりました。そこで本シリーズでは2回に渡り、Flexboxを使う際に気にかけていること、実際の案件で使用したFlexboxによるレイアウト、Flexboxに関するバグを紹介していきます。

Flexboxの基本

本記事では、Flexboxのより実践的な内容を解説します。Flexboxの仕様や基本的な使い方については、前掲の「そこが知りたい、Flexible Box」のシリーズを参照してください。

Flexboxを使う際に気にかけていること

まず、Flexboxを使う際に、筆者が気にかけていることをいくつか紹介します。

どういったときにFlexboxを使うか

Flexboxを使うとさまざまなレイアウトが可能になりますが、Flexboxでレイアウトに向いているもの・向いていないものを知っておく必要があります。まず、Flexboxでのレイアウトに向いているものを紹介しましょう。

ナビゲーション・タブ・アコーディオン・アイコンとテキストを含んだボタンなどのUI。

Flexboxでのレイアウトに向いているものは、ナビゲーション・タブ・アコーディオン・アイコンとテキストを含んだボタンなどです。これらに共通しているのは、横または縦の1つの軸に整列する直線的なものであるということです。そういったUIを作る際にFlexboxを使用します。

一方、Flexboxでのレイアウトに向いていないものです。

縦にも横にも並ぶ2つの軸で整列するといった、Webサイトのレイアウトや、グリッドシステム。

筆者は、縦にも横にも整列させるような、いわゆるグリッドシステムにFlexboxは適さないと考えます。本記事では、実例としてマルチカラムレイアウトにFlexboxを使用する例を紹介しますが、複数列になるといった、縦にも横にも並ぶ2つの軸で整列するものであれば、CSS Grid*を使用したいところです。詳細は実例で解説します。

*注:FlexboxとCSS Gridの使い分け

FlexboxとCSS Gridの使い分けに関しては、Flexboxに関する連載の「これからのグリッドレイアウト | 第6回 注意すべきポイントと使い分け」で詳しく解説していますので、本記事では簡潔に解説しています。

受託案件では採用しづらいCSS Grid

CSS Gridは、IE11が古い仕様でサポートしていたり、執筆時点で最新であるiOS 10.3でようやく対応したといった状況を考えると、対応する必要のあるブラウザが多い受託案件では積極的に採用しづらい状況になっています。ですが、近いうちにCSS Gridを当たり前に使えるようになります。ですから、目的に応じてFlexboxやCSS Gridといったレイアウト方法を使い分けられるように、今のうちから意識しておく必要があります。

flex-growやflex-shrinkの値

筆者はこれまで、flex-growflex-shrink*の値に01以外を指定したことはありません。flex-growでは、フレックスコンテナの余白が余っていたときに、その余白をどういう比率にするかの値を指定します。

*注:flex-growプロパティやflex-shrinkプロパティ

これらのフレックスアイテムに指定できるプロパティについては、次の記事を参考にしてください。

ですが筆者の経験上、余白をいい感じに割り振るようなデザインの指定をもらったことがなく、基本的にはそれぞれのフレックスアイテムの幅が%指定であったり固定値であったりします。つまり何かしらのサイズ指定があるため、flex-basisプロパティやwidthプロパティでサイズを指定します。

そういった経験から、flex-growflex-shrinkプロパティの値を次のように扱っています。

  • フレックスコンテナに余白があるときに、伸びてほしければflex-grow: 1;、そうでなければflex-grow: 0;とする
  • フレックスコンテナに余白がないとき(はみ出るとき)に、縮んでほしければflex-shrink: 1;、そうでなければflex-shrink: 0;とする

筆者の中では、真偽値のように扱っていますが、もしこの比率をうまく扱うようなデザインがあれば、1よりも大きい値を指定することがあるかもしれません。

プロパティの記述順

Flexboxはフレックスコンテナ向けのプロパティと、フレックスアイテム向けのプロパティが数多く存在します。中でもflexからはじまるプロパティは、フレックスコンテナのものと、フレックスアイテムのものが混在しています。判断しづらいプロパティには、次のようなものがあります。

  • flexプロパティはフレックスアイテムのためのプロパティ
  • flex-wrapプロパティはフレックスコンテナのためのプロパティ
  • flex-basisプロパティはフレックスアイテムのためのプロパティ

ただのフレックスアイテムであるときはあまり気になりません。しかし、次のようにフレックスコンテナかつ、フレックスアイテムである場合に、Flexbox関係のプロパティを整理して並べたとしてみましょう。

フレックスコンテナの指定とフレックスアイテムの指定の区別がつきにくいコード例

.flex-item-and-container {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  flex: 1 1 auto;
  align-items: center;
  align-self: center;
  justify-content: center;
  width: 100px;
}

一見、整理されていて見やすいと感じるのですが、フレックスコンテナの指定とフレックスアイテムの指定の区別がつきづらいのです。

そこで筆者は、フレックスアイテムであることを明示するために、プロパティの記述順を次のように、フレックスアイテムの指定が先にくるように工夫しています。

フレックスアイテムの指定を先に記述する

.flex-item-and-container {
  flex: 1 1 auto;
  align-self: center;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: center;
  /* 省略... */

フレックスコンテナの子要素は、自動的にフレックスアイテムになります。.flex-item-and-containerは、親(フレックスコンテナ)からの影響を受けている特殊な要素であるということを明示し、読み手に伝えるために、先頭に記述しています。

次のコード中のコメントアウトは実際に記述するものではありませんが、筆者の意図を伝えるとしたら次のようなコメントアウトが挿入されるでしょう。

.flex-item-and-container {
  /* 親からの影響によって指定する必要がある、自身の指定 */
  flex: 1 1 auto;
  align-self: center;
  /* 自身とその子に影響がある指定 */
  display: flex;
  flex-direction: column;

筆者は、注目してほしい・影響力の高いものを先頭に記述するようにしています。本記事で記載されているCSSのコードでも、注目してほしいものが先頭に書かれています。

紹介した記述方法は、あくまで筆者が読み取りやすい・意図が伝わりやすいと思った記述順の工夫です。必要性を感じなければどのように記述してもかまいません。

それでは、次節から実例を見てみましょう。

実例1:2カラム(1行2列)

Flexboxをよく使う場面といえば、マルチカラムレイアウトかと思います。今回の実例では、2カラム、3カラム、複数行に渡る3カラムレイアウトを紹介します。さらに、CSS Gridを使った複数行に渡る3カラムレイアウトも紹介するので、Flexboxの場合と比較して見てみましょう。

まずは、2カラムレイアウトで、どちらかが固定幅である場合です。

デモでは、1列目が固定幅となっているので、ブラウザの幅を変化させても1列目の幅は変わりません。このデモのコードは次のようになっています。

<div class="column">
  <div class="column__side">1列目</div>
  <div class="column__main">2列目</div>
</div>
.column { display: flex; }
.column__side { flex: 0 0 200px; }
.column__main { flex: 1 1 auto; }

.column__sideの幅は200pxで固定され、残りの部分は.column__mainの幅となります。このようにFlexboxを使うことで、簡単にレイアウトすることができます。2カラムとありますが、要は要素を横に整列させたい箇所でFlexboxを使っています

実例2:3カラム(1行3列)

次のデモのように、3カラムでカラム間の隙間を10pxとしたいとします。

このコードは次のようになります。

<div class="column">
  <div class="column__item">1列目</div>
  <div class="column__item">2列目</div>
  <div class="column__item">3列目</div>
</div>
.column {
  display: flex;
  margin-left: -10px; /* カラム間の隙間 */
}
.column__item {
  box-sizing: border-box;
  flex: 0 1 calc(100% / 3);
  padding: 10px;
  margin-left: 10px; /* カラム間の隙間 */
}

このデモの場合、.column__itemmargin-left: 10px;とあるので、これだけはすべての.column__itemが一行に収まりません。ですので、flex-shrinkプロパティの値(flexプロパティの2つめの値)を1とすることで、自動的に幅を調節して一行に収めることができています。

IE11とflexプロパティ中のcalc()関数

IE11ではflexショートハンドでcalc()関数を利用できない不具合があります。そのため、IE11をサポートする必要がある場合は、flexショートハンドプロパティではなく、flex-basisプロパティを使う必要があります。

.column__item {
  flex-grow: 0;
  flex-shrink: 1;
  flex-basis: calc(100% / 3);
  padding: 10px;
  margin-left: 10px; /* カラム間の隙間 */
}

実例3:複数行3カラム(複数行3列)

複数行に渡る3カラムにする場合は、フレックスアイテムを折り返させるために、flex-wrap: wrap;を指定します。

.column {
  display: flex;
  flex-wrap: wrap;
  margin-left: -10px; /* カラム間の隙間 */
}

この指定により、1行に収まらないフレックスアイテムが次の行へ配置されます。

デモを見てわかるように、3カラムとなっていません。変更前のデモでは収まりきらない場合にフレックスアイテムが収まるように縮小されていました。ですが、収まりきらない場合は次の行へ配置されるため、このように期待したレイアウトにはなっていません。そこで、少しコードを工夫したのが次のデモです。

<div class="column">
  <div class="column__item">1列目</div>
  <div class="column__item">2列目</div>
  <div class="column__item">3列目</div>
  <div class="column__item">1列目2行目</div>
  <div class="column__item">2列目2行目</div>
</div>

CSSでは、上下間の隙間を調整し、各カラムの幅から隙間の分を引くといったことをしています。

.column {
  display: flex;
  flex-wrap: wrap;
  margin-top: -10px;
  margin-left: -10px;
}
.column__item {
  box-sizing: border-box;
  flex: 0 1 calc((100% / 3) - 10px); /* カラム間の隙間の分を引く */
  padding: 10px;
  margin-top: 10px; /* カラム間の隙間 */
  margin-left: 10px; /* カラム間の隙間 */
}

しかし、CSSを見るとわかるように、若干煩雑になります。コメントを読まないと、意図が読み取りづらい部分もあるかと思います。そのため、複数行に渡るような、いわゆるグリッドレイアウトであれば、FlexboxではなくCSS Gridを使うほうがよいでしょう。

もしCSS Gridを使う場合は次のようになります。HTMLは上記と同じものです。

.column {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-gap: 10px; /* 隙間 */
}
.column__item {
  padding: 10px;
}

Flexboxに比べると、明らかにコードが簡単になっています。もちろん1行の2カラムであったり3カラムであってもCSS Gridは有用ですが、特に複数行になるときにCSS Gridが役に立つということを覚えておくとよいでしょう。

ここまでのまとめ

今回は、筆者がFlexboxを使う際に気にかけていることや実例をいくつか紹介しました。次回は引き続き実例を紹介し、Flexboxを使った際に発生するバグの対処方法についても解説します。