レスポンシブ対応のレイアウトを実装する最新テクニックを解説、モバイルファーストとデスクトップファーストの現状
Post on:2021年9月9日
モバイルファーストとデスクトップファーストの現状、それぞれのワークフローを解説し、今日のレスポンシブ対応のレイアウトを実装するより良いアプローチ方法について紹介します。
ビューポートサイズとは関係ないすべてのデバイスで共通の基本スタイルを優先的に記述する方法、メディアクエリを使用しない方法、モバイルとデスクトップの違いを考慮する必要がない方法など、最新の実装テクニックも解説します。
The State Of Mobile First and Desktop First
by Ahmad Shadeed
下記は各ポイントを意訳したものです。
※当ブログでの翻訳記事は、元サイト様にライセンスを得て翻訳しています。
- はじめに
- モバイルファースト・デスクトップファーストとは何を意味するのか
- モバイルファーストのワークフローはどうあるべきか
- デスクトップファーストのワークフローはどうあるべきか
- スコープのスタイル: より良いアプローチ方法
- レスポンシブデザインへのアプローチ方法
- ダブルブレイクポイントのメディアクエリは避ける
- デザイナーにとってのモバイルファースト
- モバイルファーストとデスクトップファーストの違いを考慮する必要がないモダンCSS
- 終わりに
はじめに
実装にモバイルファーストとデスクトップファーストのどちらを採用するか、悩んだことはありませんか?
先日私はどちらを採用しているかTwitterでアンケートをしたところ、その結果は興味深いものでした。
- モバイルファースト: 33.3%
- デスクトップファースト: 21.9%
- 両方のミックス: 24.7%
この記事では、モバイルファーストとデスクトップファーストそれぞれが何を意味するのかを探り、今日でも意味があるかを検証するとともに、今日のレスポンシブデザインの実装に役立つテクニックをいくつか解説したいと思います。
モバイルファースト・デスクトップファーストとは何を意味するのか
モバイルファーストとは、Webサイトを実装する際に、最初にスマホの小さいビューポートサイズ用のCSSを書き始め、次にCSSメディアクエリを使用して大きいビューポートサイズ(タブレットやデスクトップ)用にエクスペリエンスを変更することを意味します。
次のようなCSSを考えてみてください。
1 2 3 4 5 6 7 8 9 10 11 12 |
.section { padding: 2rem 1rem; } @media (min-width: 62.5rem) { .section { display: flex; align-items: center; gap: 1rem; padding: 4rem 2rem; } } |
スマホで異なるpaddingがあるセクションがあり、ビューポートが大きい場合はpaddingをより大きくし、flexboxのラッパーにする必要があります。
上記のCSSはごく一部ですが、Webサイトやアプリ全体の規模で想像してみてください。
モバイルファースト: スマホ用のCSSを記述してから、デスクトップ用のCSSを記述
今度はデスクトップファーストを見てましょう。CSSは逆になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.section { display: flex; align-items: center; gap: 1rem; padding: 4rem 2rem; } @media (max-width: 62.5rem) { .section { display: block; padding: 2rem 1rem; } } |
最初に大きいビューポートサイズ用のCSSを記述し、次にCSSメディアクエリを使用して小さいビューポートサイズ(スマホ)用にCSSを変更します。
デスクトップファースト: デスクトップ用のCSSを記述してから、スマホ用のCSSを記述
モバイルファーストのワークフローはどうあるべきか
デスクトップのサイズは考慮せずに、デベロッパーツールを開いて実装をはじめますか? それとも同期してはじめるべきですか? つまり、モバイルファーストでCSSを記述すると同時にデスクトップ用に拡張しますか?
2つのシナリオを考えてみました。
- まずスマホ用の全ページを実装する。スマホ用のページをすべて完成した後に、より大きいスクリーンサイズ用に拡張する。
- 並行して実装する。つまり、モバイルファーストからはじめて各セクションやコンポーネントを個別に実装する。その後に、サイズを大きくするために拡張する。
みなさんはどのように実装していますか? 私にとっては2番目の方法が優れています。理由としては、各セクションやコンポーネントを個別に処理する方が集中して作業できるからです。また、このプロセスにより、CSSを記述する際のミスを減らすことができます。
モバイルファーストではじめ、その後デスクトップ向けに拡張する場合、タブレット用とデスクトップ用のCSSを書き換える可能性があります。下記のモックアップをご覧ください。
スマホ・タブレット・デスクトップ用のUI
例としてヒーローセクションを取り上げましょう。
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 30 31 32 |
.hero { display: flex; align-items: flex-end; background-image: url('hero.jpg'); background-size: cover; background-repeat: no-repeat; } .hero__title { font-size: 1rem; } .hero__thumb { display: none; } @media (min-width: 60rem) { .hero { align-items: center; background-image: initial; background-color: #7ecaff; } .hero__title { font-size: 2rem; } .hero__thumb { max-width: 320px; display: block; } } |
スマホではヒーローに背景画像がありますが、デスクトップでは背景は無地で、右端に画像があります。ご覧のとおり、CSSはモバイルファーストでfont-sizeとbackgroundを除いてあまり上書きはされていません。
ナビゲーションの実装はどうなっているのでしょうか? モバイルファーストのアプローチではどのように見えるのでしょうか? 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
.nav { position: fixed; left: 0; top: 0; width: 100%; height: 100%; overflow-y: auto; padding-top: 2rem; /* Space for the toggle */ } .nav__toggle { position: absolute; right: 1rem; top: 1rem; } .nav__item { padding: 1rem; display: block; } .nav__item:not(:last-child) { border-bottom: 1px solid #fff; } /* Desktop styles */ @media (min-width: 60rem) { .nav { position: initial; width: initial; height: initial; overflow-y: initial; display: flex; align-items: center; padding-top: 0; background-color: blue; } .nav__toggle { display: none; } .nav__item:hover { color: blue; background-color: initial; } .nav__item:not(:last-child) { border-bottom: 0; border-left: 1px solid #fff; } } |
デスクトップ用のCSSと組み合わせたCSSの上書きの量がわかりますか? 量が多すぎて、あまりよろしくありません。
それだけではなく、これはCSSの詳細度の問題も引き起こすかもしれません。例えば、デベロッパーが下記のようなCSSで.nav__itemからborder-bottomを削除したいとします。
1 2 3 |
.nav__item { border-bottom: 0; } |
この場合、:not疑似クラスの方がより高い詳細度のため、機能しません。
:not疑似クラスを使用するため、より高い詳細度になります。
下記のいずれかを使用した場合にのみ機能します。
1 2 3 4 5 6 7 8 9 10 |
.nav .nav__item { border-bottom: 0; } /* Or */ .nav__item:not(:last-child) { border-bottom: 0; border-left: 1px solid #fff; } |
デスクトップファーストのワークフローはどうあるべきか
モバイルファーストと同じUIで、今度はデスクトップファーストを見てましょう。
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
.nav { display: flex; align-items: center; background-color: blue; } .nav__toggle { position: absolute; right: 1rem; top: 1rem; } .nav__item { padding: 1rem; display: block; } .nav__item:hover { color: blue; background-color: initial; } .nav__item:not(:last-child) { border-bottom: 0; border-left: 1px solid #fff; } @media (max-width: 25rem) { .nav { display: block; position: fixed; left: 0; top: 0; width: 100%; height: 100%; overflow-y: auto; padding-top: 2rem; /* Space for the toggle */ } .nav__toggle { display: block; } .nav__item:not(:last-child) { border-bottom: 1px solid #fff; } } |
デスクトップファーストの方がモバイルファーストに比べて上書きが少なくなります。これは意外だと思いませんか? その理由はmax-widthメディアクエリを使用して特定のデザインを特定のビューポート幅にスコープしているからです。
モバイルファーストでCSSを記述し、デスクトップ用に上書きするのではなく、その逆をおこなっています。
CSSの量を視覚的に比較してみましょう。
デスクトップファーストの方が量が少なく、上書きも少ないことに注目してください。
上: モバイルファーストのCSSの量
下: デスクトップファーストのCSSの量
スコープのスタイル: より良いアプローチ方法
はい、あります。私の場合、特定のアプローチに固執することはありません。むしろ、両方をミックスするのを好みます。つまり、基本となるスタイルを記述し、それからスマホとデスクトップで何が起こるかを考え始める必要があります。Elad Shechterの記事で、「基本を優先する」という方法が気に入っています。
実際にCSSで見てましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.nav { /* 基本スタイル: ビューポートサイズとは関係ない */ } /* Desktop styles */ @media (min-width: 800px) { .nav { ... } } /* Mobile styles */ @media (max-width: 799px) { .nav { ... } } |
ご覧のとおり、スタイルはビューポートのサイズごとにスコープされています。つまり、上書きする必要はありません。このアプローチはスマホとデスクトップで見た目が異なるコンポーネントの実装に役立ちます。
しかし、<section>などは多くの場合スマホとデスクトップのスタイルが非常に似ているため、ミックスしたアプローチを使用しても意味がありません。
1 2 3 4 5 6 7 8 9 10 |
.section { padding: 1rem; } /* Desktop styles */ @media (min-width: 800px) { .section { padding: 2rem 1rem; } } |
レスポンシブデザインへのアプローチ方法
ある日を境に、モバイルファーストとデスクトップファーストの議論はそれほど重要ではなくなったと感じています。現在のCSSでは、メディアクエリのないCSSを使用してレスポンシブ対応のレイアウトを実装できます。
そうは言っても、モバイルファーストとデスクトップファーストの議論は、ビューポートサイズによって大きな違いがある複雑なコンポーネントを除き、特定のビューポートサイズで特定の要素を表示・非表示にするかどうか(ナビゲーションのトグルボタンなど)だけになると思います。
実際の例を挙げて、このコンセプトを解説します。
よくあるUI
スマホとデスクトップで大きな違いがあるコンポーネントは、ヘッダとナビゲーションです。その他のコンポーネントは若干の違いがあるだけです。ヘッダの実装はmin-widthとmax-widthのメディアクエリを組み合わせることで、目的のビューポートサイズに合わせて各デザインのスコープを設定できます。
しかし、ヒーローセクションと記事のグリッドはベーススタイルを持ち、必要に応じてmin-widthを使用します。
このようなデザインは、どのような考え方で作られているのでしょうか? 厳密なことは何もありません。わたし達が手にしているデザインについてです。では、もう少しディテールを見てみましょう。
ナビゲーションのスマホでの表示
モバイルファーストのコンセプトをヘッダとナビゲーションに採用した場合、多くのCSS上書き(別名: 重複)が発生してしまいます。これはよろしくありません。ヘッダとナビゲーションのCSSは下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
.header { /* Base styles */ } /* Desktop styles */ @media (min-width: 1000px) { .nav__toggle, .nav__close { display: none; } } /* Mobile styles */ @media (max-width: 999px) { .nav { position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: #4777dB; } } |
ヒーローセクションはflexboxを使用して、行と列のスタイルを処理したり、要素の並べ替えや移動をすることができます。
1 2 3 4 5 6 7 |
<section class="hero"> <div class="wrapper"> <img src="thumb.jpg" alt="" /> <h2><!-- Headline --></h2> <p><!-- Description --></p> </div> </section> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.hero { display: flex; flex-direction: column; } @media (min-width: 1000px) { flex-direction: row; } @media (max-width: 999px) { .hero__thumb { order: -1; } } |
max-widthメディアクエリでスコープを設定して、order: -1;を1度だけ記述していることに注目してください。私は下記のように記述することもできます。
1 2 3 4 5 6 7 8 9 |
.hero__thumb { order: -1; } @media (min-width: 1000px) { .hero__thumb { order: initial; } } |
しかし、重複していることがわかりますか? flexboxでorderを使用してしまうと、見た目の順序とHTML内の要素の順序が一致しないので注意が必要です。
このプロセスを説明する視覚的なチャートです。
プロセスを説明する視覚的なチャート
ダブルブレイクポイントのメディアクエリは避ける
min-widthとmax-widthのメディアクエリに同じ値を使用すると、気付きにくい・デバッグしにくい問題が発生します。
1 2 3 4 5 6 7 8 9 10 11 |
@media (max-width: 500px) { .nav { display: none; } } @media (min-width: 500px) { .nav__toggle { display: none; } } |
こういったメディアクエリをあなたはよく見かけるかもしれません。しかし、このCSSでは重要なブレークポイントである500pxでの検証を忘れています。2つのブレークポイントに1pxのギャップがあります。上記のCSSでは、500pxの時にナビゲーションもトグルも表示されません。
499pxと501pxでは期待通りに表示されるが、500pxでは何も表示されない
この1pxは、デベロッパーツールのモバイルモードで500pxのメディアクエリの値を手動で入力しないとデバッグが困難です。この問題を防ぐには、2つのメディアクエリで同じ値を使用しないようにしてください。
1 2 3 4 5 6 7 8 9 10 11 |
@media (max-width: 499px) { .nav { display: none; } } @media (min-width: 500px) { .nav__toggle { display: none; } } |
この問題は、私の著書「Debugging CSS」の43ページから拝借しました。
デザイナーにとってのモバイルファースト
私自身もデザイナーですが、さまざまな理由からモバイルファーストでデザインすることは好きではありません。
- 制限があり、クリエイティブを発揮するのが難しい。
- 高さが大きいデザインで作業すると、上下にスクロールし続けることになり、イライラする。
その一方、少なくとも私にとっては、デスクトップファーストでデザインする方がはるかに優れています。アイデアをすぐに試すことができますし、上下にスクロールしなくてもデザイン全体を見渡すことができます(モバイルファーストでデザインしている場合)。
デスクトップファーストでデザインを始め、スマホ用にデザインを変更することもできます。それが良い出発点となり、残りのページをモバイルファーストにしても構わないと思います。しかし、新規のプロジェクトでは、モバイルファーストでデザインを始めることは好ましくありません。
モバイルファーストとデスクトップファーストの違いを考慮する必要がないモダンCSS
最近ほどCSSの書き方が上手になったことはありません。レスポンシブデザインをはるかに簡単にする、現在および今後のCSS機能がたくさんあります。
flexboxのラッピング
Geoffreyの記事「How to Make a Media Query-less responsive Card Component」の中で、メディアクエリを使用せずにレスポンシブのカードコンポーネントを実装するテクニックを解説しています。ここではその基本的なコンセプトを説明しますが、詳細は記事をお読みください。
メディアクエリを使用せずにレスポンシブのカードコンポーネント
flex-basisに固定値を定義し、必要に応じてアイテムを拡大および縮小できるようにすると、メディアクエリを使用せずにコンポーネントを実装できます。
スクリーンサイズが小さい場合は、下記のように表示されます。
スクリーンサイズが小さい場合の表示
CSS Gridのminmax()
CSS Gridのおかげで、メディアクエリに依存しないレスポンシブのグリッドレイアウトが可能になりました。下記の例を考えてみましょう。
1 2 3 4 5 |
.wrapper { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 1rem; } |
これは、各アイテムに200pxの最小幅を与えるレスポンシブのグリッドです。CSS Gridを使用しない場合は、メディアクエリを使用してビューポートサイズに応じて幅を変更する必要があります。
minmax()についてさらに詳しく知りたい時は、下記をご覧ください。
- しっかり理解しておくと便利なCSSのテクニック、minmax()関数の使い方
- CSSの関数はどんどん便利になっている!minmax()を使うとMedia Queries無しでレスポンシブが簡単に実装できる
ビューポート単位と比較機能
CSSのビューポート単位とCSSの比較機能を組み合わせて使用することで、フォントサイズ、パディング、マージン、一部の要素のサイズなどを変更する必要性を減らすことができます。
1 2 3 4 5 6 7 8 9 10 11 |
.title { font-size: clamp(16px, (1rem + 5vw), 50px); } .hero { padding: clamp(2rem, 10vmax, 10rem) 1rem; } .sidebar { flex-basis: max(30vw, 150px); } |
コンテナクエリ
Chrome Canaryで、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 |
.wrapper { contain: layout inline-size; } @container (min-width: 250px) { .pagination { display: flex; flex-wrap: wrap; gap: 0.5rem; } .pagination li:not(:last-child) { margin-bottom: 0; } } @container (min-width: 500px) { .pagination { justify-content: center; } .pagination__item { display: block; } } |
デモページ(Chrome Canaryでご覧ください)
終わりに
これはほんの始まりに過ぎません。この記事で見てきたようにモダンCSSを使用すると、メディアクエリを使用しなくてもレスポンシブ対応のレイアウトを実装することができます。ですから、モバイルファーストとデスクトップファーストのどちらを選択するかについて、そこまで考える必要があるのかということです。
この記事があなたのお役に立てれば幸いです。
コメントや提案があれば、@shadeed9までお願いします。
sponsors