コハム

Webクリエイターのコハムが提供する、Web制作に役立つ情報をまとめたブログです。

メディアクエリはもう古い?CSS数学関数やコンテナクエリによる次世代のレスポンシブ対応とは?

Beyond CSS Media Queries

記事は上記記事を意訳したものです。

※当ブログでの翻訳記事は元サイト様に許可を得て掲載しています。


メディアクエリの問題点

メディアクエリはCSSほどの長い歴史を持っており、フレックスボックス、グリッド、レスポンシブユニット、数学関数がない時代から、多少レスポンシブなウェブサイトを作る最も実用的な選択肢でした。

2010年代初頭には、モバイルデバイスの普及とEthan Marcoetteの古典的な記事「Responsive Web Design」の発行により、メディアクエリはレイアウトをスクリーンやデバイスに合わせて変形させるために不可欠なものになりました。CSSフレックスボックスやグリッド仕様が登場した時でさえ、メディアクエリを使ってリサイズしたレイアウトは残り続けました。

メディアクエリの実際の使用状況に関するデータは得られませんが、ビューポートサイズだけでなく、ユーザー設定などにまで機能が拡張されたことから、メディアクエリはレスポンシブデザインの主要な要素であり続けています。

現在、CSSにはレイアウトを様々な条件(ビューポートサイズ以外)に合わせて調整できるオプションとツールがあり、フレックスボックスやグリッドはもちろん、レスポンシブな長さの単位、そしてコンテナクエリ(後で説明します)などです。

しかし、メディアクエリはまだ開発者が手っ取り早く使う方法であり続けています。習慣や一貫したブラウザサポートの欠如、あるいは新しいアプローチを取り入れるのが遅れているためかもしれません。

メディアクエリに賛成です。ビューポートサイズを監視するだけでなく、OSの設定や入力デバイスなど、より良くてアクセシブルなユーザー体験を実現する重要な役割を果たしています。

しかしながら、レスポンシブレイアウトにおいてメディアクエリが金字塔であり続けるべきでしょうか?状況によりますが、メディアクエリはアクセシビリティの解決に重点を置くように進化し、レスポンシブ性に関する責任をほかのCSSの機能に譲り渡してきています。

メディアクエリの問題点

メディアクエリは、ほとんどのレスポンシブ関連の問題に見えた良い解決策でしたが、ウェブが大規模で複雑なレイアウトに成長するにつれ、メディアクエリの限界がより顕著になってきました。

問題1: ビューポート中心主義

レイアウトを調整するためのメディアクエリのブレークポイントを書く時、利用できるのはビューポートの幅や向きなどのプロパティのみです。フォントサイズを変更するだけなら、ビューポートは最良の選択肢ですが、ほとんどの場合はコンテキストが重要です。

ページ上のコンポーネントは、通常のドキュメントフローに従って他のコンポーネントとスペースを共有し、相対的に配置されています。利用できるのがビューポート幅のみの場合、特定のブレークポイントを設定する場所を正確に知ることは、妥協のタスクとなり、一部のコンポーネントは調整されたレイアウトに適切に対応しますが、他のコンポーネントでは同じブレークポイントで更なる調整が必要になります。

つまり、ブラウザをリサイズし、コンテンツが詰まり過ぎる前の適切なブレークポイントを探すことになります。

問題2: 管理が困難

現在、すべてがコンポーネントです。ノマドのように、コンポーネントは場所を移動し、他のコンポーネントと空間を共有し、可変的なコンテンツを持ち運びます。再び述べますが、メディアクエリはコンポーネントを取り巻くコンテキストを全く認識していません。開発者は、それぞれのケースでスウィートスポットを見つける責任があります。

これは面倒な作業です。なぜなら、メディアクエリの理想的なブレークポイントは、ページをリサイズして見つけるハードコーディングされた"マジックナンバー"のようなものだからです。また、それはコンポーネントを取り巻くコンテキストによって異なるため、複数のメディアクエリが必要になります。コンポーネントのコンテンツやコンテナを変更する場合は、メディアクエリも変更する必要があります。

スタイルシートの中でメディアクエリをどこに管理するかについても、開発者によって異なります。終盤に置く人もいれば、パーシャルファイルで管理し、プリプロセッサにコンパイルさせる人もいます。

最近のCSSネスティング機能により、同じスタイルルール内にメディアクエリを付けることができ、よりクリーンになりました。しかし、システム内の各コンポーネントに対して行う必要があり、編集時に考慮すべきインスタンスが増えてしまいます。これがメディアクエリをレスポンシブの銀の弾丸とするもう1つの問題につながります。

問題3: それほどレスポンシブではない

ほとんどの場合、要素を液状化してスクリーンに合わせてリサイズしたいと思います。しかし、特定のブレークポイントで「破損する」ごとに新しいメディアクエリを書くのは、大変な作業です。コンポーネントが960px〜970pxの間の狭い画面で高すぎる場合、本当にその特定の状況のためだけに新しいスタイルセットを書く必要があるのでしょうか?

私はそれをレスポンシブデザインとは呼びません。固定値に基づく何らかの適応型デザインです。そこには流動性がありません。

メディアクエリは不要(リサイズには)

幸いにも、私たちは2012年の時代を超えて生きており、メディアクエリよりも優れたオプションがたくさんあります。フレックスボックス、グリッド、レスポンシブユニット、数学関数などはほとんどのCSS開発者に採用され、広く普及しています。一方、コンテナクエリなどはまだ初期段階ですが、私が新しい技術について説明する必要はありません。既にそれらの存在を知っていると確信しています。しかし、メディアクエリはリサイズのためにCSSに組み込まれる傾向にあり、特に、clamp()関数やレスポンシブユニットを使った創造的な考え方で、メディアクエリよりも優れた方法がある状況があります。

私の焦点は、既存のメディアクエリをモダンなレスポンシブ手法に置き換えることにあります。

フレックスボックス

フレックスボックスとメディアクエリは、しばしばペアで使われます。フレックスボックスがある方向でレイアウトを決め、メディアクエリは特定のビューポート幅で方向を変えるのに使われます。

このシンプルですが一般的なパターンは、先ほど挙げた3つの問題すべてに直面します。

  • ビューポート中心主義 コンテナの方向が変わる場所を選ぶ際、ビューポート幅のみを考慮しています。私はテストの結果、700pxがマジックナンバーのブレークポイントだと判断しました。
  • 再利用と管理が難しい ビューポート幅のみを考慮しているため、要素を小さなコンテナの中で使うことができず、要素の内容や周りのコンテンツが変わると、レイアウトが適切でなくなる可能性があります。
  • それほどレスポンシブではない 700pxがブレークポイントなので、その閾値を超える狭い画面のデバイスではコンテンツが詰め込まれすぎる可能性があり、一方で他のデバイスでは最適な表示になります。
    より多くのメディアクエリを追加して修正しようとすれば、問題2に逆戻りです。

この場合の最良の解決策は、メディアクエリを完全に避けることです。flex shorhandプロパティを使って、使用可能な空間に応じて

要素が伸縮するようにし、最小幅を400pxに設定します。

main {
  display: flex;
  flex-flow: row wrap;
}

main article {
  flex: 1 1 400px;
}

前のメディアクエリの例を翻訳すると、「ビューポート幅が700px未満の場合、要素を折り返す」となります。しかし、その理由は不明です。クエリはその要素の文脈を無視しています。一方、更新された例を翻訳すると、「その要素がどこにあっても、利用可能な空間に応じて最大400pxまで拡大し、空間が変わればサイズを調整する」となります。

次のデモをリサイズしてみてください。画面サイズの変化に合わせて、articleがスムーズに流れていく様子がわかります。

そして、マジックナンバーなしでコードを減らすことができました。

グリッド

前の例では、最後の flexible な要素が最終行で利用可能な全ての空間を取ることができてしまいます。すべての flexible 要素を同じサイズにしたい場合は、その要素の幅を設定し、またメディアクエリを使う必要があるかもしれません。柔軟な要素に幅を設定する場合は、グリッドに切り替える方が適していることが多くあります。グリッドでは列や行のトラックを明示的に設定できるためです。

幸いにも、わずか2行のCSSで実現できます。

main {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); 
}

簡単に説明すると:

  • auto-fit は利用可能なスペースにできるだけ多くの列を合わせ、残りのスペースがあればそれらを拡大します。
  • minmax は列の最小幅を指定します。この例では500pxです。

メディアクエリを使わずにレスポンシブなレイアウトを作れることがわかりますね。

数学関数とレスポンシブユニット

数学関数とレスポンシブユニットは、リサイズに関するほとんどの問題を解決します。特定のブレークポイントを面倒に定義することなく、レスポンシブな制限を設定できます。モダンブラウザで完全にサポートされており、すでに広く使われています。

min()関数を使うと、レスポンシブユニットのビューポート幅(vw)や相対単位のパーセンテージに基づいて、要素が過剰に伸びすぎないよう上限を設定できます。次の要素は親要素の幅いっぱいに広がろうとしますが、300pxを超えて伸びることはありません。

.min {
  height: 400px;
  width: min(100%, 300px);
}

aspect-ratioプロパティを使えば、高さを幅に従って変更できます。

.min-and-aspect-ratio {
  aspect-ratio: 1/1; /* または 1 */
  width: min(100%, 300px);
}

max()関数を使うと、下限を設定できます。次のCSSでは、要素のサイズが親要素の半分まで拡大できますが、300px以下には縮小しません。

.max {
  height: 400px; 
  width: max(50%, 300px);
}

少し分かりにくいですね? 最大幅を設定するにはmin()を使い、最小幅を設定するにはmax()を使います。

そしてたいへん人気のclamp()関数は、最大値と最小値の両方を設定できるだけでなく、理想的なサイズを中央の引数として指定できます。「クランプ」には一定の範囲があり、その範囲内でターゲットサイズに近づこうとします。

次のデモの要素は、親要素の利用可能な幅いっぱいに広がろうとしますが、300px以上にも200px以下にもなりません。

組み合わせが大切

デバイスに関係なく見栄えの良いウェブサイトを作るには、レスポンシブユニットや数学関数だけでは不十分です。すべての手法を組み合わせてシームレスなレスポンシブ体験を作る必要があります。JavaScriptのPerformance APIのように、パフォーマンス関連の作業のために一緒に機能する一連の標準があると考えるとよいでしょう。

私たちが持っているのは、レスポンシブデザインのためのCSSの仕様群です。これらはメディアクエリに置き換わるものではありませんが、追加されたもので、レスポンシブインターフェイスの構築に最適な組み合わせで機能するように設計されています。

例えば、ビューポート幅に応じてフォントサイズを増減させたい場合があります。メディアクエリを使えば簡単に実現できますが、プロジェクトによってはレスポンシブユニットを使う方が効率的で保守性が高いかもしれません。

確かに、特定のブレークポイントでフォントサイズを固定の値に更新するためにメディアクエリを使うことができます。適切なサイズを得るには複数のブレークポイントが必要になる可能性がありますが、有効な方法です。

代わりに、レスポンシブユニットを使ってフォントサイズを更新します。例えばvwユニットはビューポート幅に対する相対値で、1vwは現在のブラウザ幅の1%に相当します。

しかし、さらに進んで、ビューポート単位とclamp()関数を組み合わせることで、最小・最大値を設定し、理想的なサイズを定義できます。

しかし、さらによい方法があります。要素のfont-sizeにこれを直接宣言すれば、他のすべてのフォントが同じ係数でリサイズされます。そして、remユニットを使えば、各要素のフォントサイズの大きさを指定したり、clamp()を使ったり、特定の要素では固定のピクセル値を使ったりできます。

remユニットとemユニットの違いは、前者は「ルート」要素つまりに対する相対値で、後者はその要素の親要素に対する相対値だということに注意してください。

これらの手法は単独で使うものではなく、メディアクエリの完全な代替にもなりません。レスポンシブなインターフェースを構築するには、ハンマー、レンチ、くぎ、ねじのようないろいろな道具が必要なのです。

こんにちは、コンテナクエリ

メディアクエリはページ全体のレイアウトを変更するのが得意です。例えば、ショッピングカートを考えてみましょう。カートを広く表示できる十分な幅があれば、製品を

で表示し、ゆったりとした配置にできます。

しかし、同じレイアウトをモバイルで使うのは適切ではありません。テーブルそのものにレスポンシブの課題がありますが、モダンな手法を使えば、あまりいじくり回らずに別のレイアウトを検討できるかもしれません。

これは単に幅や高さを変更するだけでなく、要素の表示/非表示、境界線の色、フレックスの方向の変更など、多くの調整が必要です。そしてこれらはメディアクエリでしか実現できないのでしょうか?確かに、ビューポートサイズに応じてレイアウトを完全に切り替える必要がある場合でも、コンテナクエリを使ってより適切に実現できます。

メディアクエリの問題点の1つは、要素の周りの文脈を全く考慮せずにビューポートサイズのみを基準にしていることです。

もし、要素がページ全体の幅を取ることができれば、ページの幅とビューポートの幅は密接に関係するので、メディアクエリを使って調整するのは完全に妥当でしょう。

しかし、それらの要素を複数の狭いカラムに含めたい場合はどうでしょうか。

要素の隣に