CSS Gridが適しているレイアウト、Flexboxが適してるレイアウトを詳しく解説
Post on:2020年3月28日
CSS GridとFlexboxは、CSSで現在主流となるレイアウトのテクノロジーです。
CSS GridとFlexboxは表面的には似ているように感じるかもしれません。しかし、実際には異なるタスクに使用され、それぞれ異なるレイアウトの問題を解決します。
Flexboxが適してる場合、CSS Gridが適している場合、また両方を使用する場合、さまざまなレイアウトの問題を正しく解決する方法を紹介します。
Grid vs. Flexbox: Which should you choose?
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
ページ全体のレイアウト
CSS Gridはコンテナベースで、Flexboxはコンテンツベースです。Flexboxのレイアウトではセル(Flexアイテム)のサイズはFlexアイテム自身で定義され、CSS Gridではセル(Gridアイテム)のサイズはGridコンテナで定義されます。
実際のコードを見ながら、詳しく説明します。
水平方向にdiv要素を配置するためのHTMLを用意しました。
1 2 3 4 5 6 |
<div class="row"> <div>1</div> <div>2</div> <div>3</div> <div>4</div> </div> |
Flexboxを使用して、スタイルしてみます。
1 2 3 4 5 6 7 8 9 10 11 |
.row { margin: 20px auto; max-width: 300px; display: flex; } .row > div { border: 1px dashed gray; flex: 1 1 auto; /* Size of items defined inside items */ text-align: center; padding: 12px; } |
「flex: 1 1 auto;」で、Flexアイテム内のセルのサイズが定義されています。flexプロパティでは順にflex-grow、flex-shrink、flex-basisプロパティを設定することを省略しており、デフォルト値は「0 1 auto」です。
.rowのdiv要素はFlexコンテナで、そこでアイテムのサイズを定義していないことに注目してください。Flexアイテムでサイズを定義しています。
参考: CSS Flexbox の各プロパティの使い方を詳しく解説
上記のコードは、下記のように表示されます。
ブラウザで表示
今度は、CSS Gridでスタイルしてみます。
1 2 3 4 5 6 7 8 9 10 11 |
.row { margin: 20px auto; max-width: 300px; display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; /* Size of items defined inside container */ } .row div { border: 1px dashed gray; text-align: center; padding: 12px; } |
このスタイルシートはFlexboxで実装したのと全く同じ見た目になります。
ここで注目すべきは、セルのサイズを定義しているのは.rowのdiv要素、つまりGridアイテムではなく、Gridコンテナであることです。
これは非常に重要な違いです。
Flexboxのレイアウトではコンテンツがロードされた後に計算されるのに対して、Gridのレイアウトではコンテンツに関係なく計算されることを示しています。
つまり、Flexboxでページ全体のレイアウトを構築することは表示が遅くなるため、避けた方がよいということです。
隙間が必要なレイアウト
CSS GridとFlexboxの大きな違いは、CSS Gridにはgrid-column-gapを使用してGridアイテムに隙間を作ることができることです。Flexboxには隙間のためのプロパティはありません。
隙間を使ったレイアウト
Flexboxで同様の結果を得るには、paddingとネストされたコンテナを使用するか、Flexコンテナの幅を広げてjustify-contentを使用する必要があります。
いずれは、CSS Box Alignment Module 3に用意されているマルチカラムのcolumn-gapプロパティを利用できるようになるかもしれません。
1次元と2次元のレイアウト
1次元とは一方向(横行か縦列のいずれか)に配置することで、2次元とは二方向(横行と縦列)に配置することです。これはtableレイアウトの時代からあるコンセプトで、CSS GridもFlexboxもこのコンセプトに基づいています。
1次元に要素を配置するのはFlexboxが適しています。そして2次元に要素を配置するのはCSS Gridが適しています。
Flexboxは1次元、CSS Gridは2次元、と覚えておいてもよいでしょう。よく使用される1次元のレイアウトを見てましょう。
1次元のレイアウト
HTMLはリスト要素を使用しました。
1 2 3 4 5 6 7 8 |
<ul class="social-icons"> <li><a href="#"><i class="fab fa-facebook-f"></i></a></li> <li><a href="#"><i class="fab fa-twitter"></i></a></li> <li><a href="#"><i class="fab fa-instagram"></i></a></li> <li><a href="#"><i class="fab fa-github"></i></a></li> <li><a href="#"><i class="fas fa-envelope"></i></a></li> <li><a href="#"><i class="fas fa-rss"></i></a></li> </ul> |
Flexboxでリストの各アイテムを1次元(横行)に配置します。
1 2 3 4 5 |
.social-icons { display: flex; list-style: none; justify-content: space-around; } |
justify-contentプロパティは、Flexコンテナの余分なスペースがFlexアイテムにどのように分配されるかを決定します。space-aroundはFlexアイテムが均等に配置されるようにスペースを分配します。
参考: CSS Flexbox の各プロパティの使い方を詳しく解説
続いて、よく使用される2次元のレイアウトを見てましょう。
2次元のレイアウト
このレイアウトは、単一の行または単一の列で実装することはできません。複数の行と列が必要です。2次元のレイアウトなので、CSS Gridで実装します。
1 2 3 4 5 6 |
<div class="container"> <header>Header</header> <main>Main</main> <aside>Aside</aside> <footer>Footer</footer> </div> |
CSSは下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
.container { max-width: 800px; margin: 2em auto; display: grid; grid-template-columns: 3fr 1fr; grid-template-rows: repeat(3,auto); grid-gap: 1rem; } .container header { grid-area: 1/1/2/3; } .container main { grid-area: 2/1/3/2; } .container aside { grid-area: 2/2/3/3; } .container footer { grid-area: 3/1/4/3; } .container > * { background-color: #ddd; padding: 1rem; } |
grid-template-columnsプロパティで2つの列を作成し、grid-template-rowsプロパティで3つの行を作成しています。repeat()関数で、3つの行の高さを作成します。
Gridアイテム(header, main, aside, footer)の内部では、grid-areaプロパティを使用してそれぞれのGridアイテムがカバーする領域の大きさを定義します。
参考: CSS Gridの実装で必要な基礎知識、主要なプロパティと用語をくわしく解説
ラッパー
コンテナ内のアイテムの幅の合計がコンテナの幅より大きい場合、どちらのレイアウトモデルにもアイテムを新しい行に押し出すオプションがあります。しかし、その方法はそれぞれ異なります。
サンプルのレイアウトでその違いを見てましょう。
FlexboxとCSS Gridでそれぞれ6つのdiv要素を配置します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<h2>Flexbox</h2> <div class="row-flex"> <div>1 2 3 4 5 6 7 8 9 0</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <div>6</div> </div> <h2>Grid</h2> <div class="row-grid"> <div>1 2 3 4 5 6 7 8 9 0</div> <div>2</div> <div>3</div> <div>4</div> <div>5</div> <div>6</div> </div> |
FlexboxとCSS Gridのコードは、下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* Flexbox row styles */ .row-flex { margin: 40px auto; max-width: 600px; display: flex; flex-wrap: wrap; } .row-flex div { border: 1px dashed gray; flex: 1 1 100px; text-align: center; padding: 12px; } /* Grid row styles */ .row-grid { margin: 40px auto; max-width: 600px; display: grid; grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); } .row-grid div { border: 1px dashed gray; text-align: center; padding: 12px; } |
Flexboxでは「flex: 1 1 100px;」を使用して、Flexアイテムに100pxのベース幅を与えて、それを伸縮させます。そして、flex-wrapプロパティにwrapを設定することで、Flexコンテナ内のFlexアイテムのラッピングも可能にしています。デフォルト値はnowrapです。
CSS Gridでは、grid-template-columnsプロパティを使用して、minmax()関数によって設定された最小幅100pxの列を作成しています。そして、repeat()関数を使用して列を繰り返し作成しています。
アイテムの幅の合計がコンテナの幅より大きい場合に、どうなるか見てみてください。
コンテナの幅を変更
CSS GridとFlexboxの便利な点は、利用可能なスペースの量に基づいてアイテムを収縮して絞り込む能力にあります。Flexboxはflex-growプロパティとflex-shrinkプロパティを使用してこれを実現し、CSS Gridはgrid-template-columnsプロパティ内でminmaxとauto-fill関数の組み合わせを使用してこれを実現します。
セル5とセル6が押し下げられた時に、注目です。
Flexboxではセル5とセル6は他のセルと同じ幅ではなく、CSS Gridではすべてのセルと同じ幅を維持しています。
これは、Flexアイテムが新しい行に押し出された時に、Flexboxレイアウトのアルゴリズムがそれを別のFlexコンテナの一部として扱うためです。従って、押し出されたアイテムはそのコンテキストを失います。
この振る舞いは、下記のようなフォームに適しています。
このようなフォームに適している
フォームのコードです。
1 2 3 4 5 6 7 |
<div class="subscriber-form-container"> <form> <input type="email" placeholder="Email Address"> <input type="text" placeholder="Name"> <input type="submit" value="Subscribe"> </form> </div> |
Flexboxで実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
.subscriber-form-container { max-width: 650px; margin: 40px auto; border: 1px dashed gray; box-sizing: border-box; } .subscriber-form-container form { display: flex; flex-wrap: wrap; } .subscriber-form-container form input { margin: 6px; padding: 0.4rem; box-sizing: border-box; } .subscriber-form-container form input{ flex: 1 1 150px; } .subscriber-form-container form input[type="email"] { flex: 2 1 300px; } |
メールのフィールドの幅は他の2つの入力要素の幅の2倍にすることが望ましく、これはflex-growとflex-basisで実現しています。これで余分なスペースがある場合、メールのフィールドは他の入力要素に比べて2倍の幅になります。
Flexboxはこのようなケースでは、CSS Gridより優れています。
確かにCSS Gridでminmax()関数を使ってこの振る舞いに近いものを実装することができますが、Flexboxはこのような一次元レイアウトに適しています。
ただし、画像ギャラリーのように改行されても幅を保持したいレイアウトが必要な場合は、CSS Gridが最適です。
押し出されたアイテムの幅を保持
ここまでのレイアウトに、Media Queriesが使用されていないのに気がついていましたか? CSS GridもFlexboxもレスポンシブのコンセプトをベースにしているので、Media Queriesを減らすことができます。
CSS Gridは将来、Flexboxを時代遅れにするでしょうか?
それは絶対違います。
この記事を読んだら分かると思いますが、CSS GridとFlexboxはどちらも異なる一連の問題を解決するように設計されています。
CSS Gridは2018年現在、採用するために十分なブラウザのサポート状況ではないと言えるでしょう。目安としては、95%がサポートしている必要があります。Flexboxは95%がサポートしていますが、CSS Gridは87%となっています。
CSS Gridが満足なサポート状況になる日もかなり近いと思います。CSS GridとFlexboxを組み合わせ、以前は不可能だった素晴らしいレイアウトを作成することができます。
sponsors