メディアクエリだけではない、 レスポンシブ対応の考え方と実装 2025.09.21 フロントエンドカンファレンス東京 池奥裕太 エムスリー株式会社 エンジニアリンググループ
0 今日お話しすること 結論 レスポンシブ対応のことを考えるときは、サイズの変化をどこで吸収 するべきかを考えよう flexboxやコンテナークエリーなどを使ってスコープを絞ろう 実装 デザイン この辺の話
0 今日お話しすること 目次 なぜメディアクエリを使いたくないのか コンポーネント指向なフレームワーク 可変な部分と不変な部分を意識する flex, grid などのCSS領域での工夫をする コンテナークエリーの紹介 まとめ メンテナンスコストと対応法方法
0 レスポンシブ対応の難しいところ レスポンシブ対応はコードがスパゲッティ化しやすい MediaQueryやif文が散在して見通しが悪い... モバイル向け・PC向けで2重実装になってしまう... 複数パターンで動作確認をする必要があり管理コストが高い... PCとモバイルを2元論(タブレットも含めて3元論?)で考えるような考え方は、 複雑なフロントエンドの世界とは噛み合いづらい
0 レスポンシブ対応の難しいところ 理由1: コンポーネント志向なUIフレームワーク・開発手法 React, Svelte, Vueなど、 コンポーネント志向なフレームワークがメインストリームに。 Atomicデザインや、それにインスパイアされたコンポーネント設計が主流。 画像: https://atomicdesign.bradfrost.com/chapter-2/ より
0 レスポンシブ対応の難しいところ 理由1: コンポーネント志向なUIフレームワーク・開発手法 Component UI = f(state) 同じstateに対して、同じUIが描画されることを期待 ※ このstateに、ウィンドウサイズは含まれない Prop A Component
0 レスポンシブ対応の難しいところ 理由1: コンポーネント志向なUIフレームワーク・開発手法 UI = f(state) 同じstateに対して、同じUIが描画されることを期待 ※ このstateに、ウィンドウサイズは含まれない 再利用を前提としたコンポーネントは、それが 受け取る値のみに依存し、コンテクストに関心 を持たない。 ウィンドウサイズ = Rootコンポーネントが 持つ最大のコンテクスト ウィンドウサイズに依存したコンポーネント は管理しづらい Component
0 レスポンシブ対応の難しいところ 理由2: 表示領域のサイズは連続的に変化する https://www.apple.com/jp/os/ipados/ より
0 レスポンシブ対応の難しいところ 理由2: 表示領域のサイズは連続的に変化する Webサイトを見るデバイスは様々 タブレットのSplit View, Multi Window 折りたたみ式スマートフォン ウルトラワイドモニタ 今後登場するであろう様々なサイズのデバイス → 固定的な分類を得ることは難しい https://www.apple.com/jp/os/ipados/ より
0 今日お話しすること 突然ですが、 Webサイトの要素はコンクリートとバネでできている!! コンクリート バネ
1 可変な部分と不変な部分を切り分ける 表示領域の変化に対してどう対応するか 可変な部分と、不変な部分を切り分ける
1 可変な部分と不変な部分を切り分ける 表示領域の変化に対してどう対応するか 可変な部分と、不変な部分を切り分ける サイズが変化しても ユーザービリティへの影響が小さい サイズが変化するとユーザービリティが 著しく低下する
1 可変な部分と不変な部分を切り分ける 余白の大きさで吸収する 余白の大きさでレイアウトが崩れることはほぼない → 余白の拡縮で、表示領域の差分を吸収する 例)X, connpass, github, メディアサイトなど
1 可変な部分と不変な部分を切り分ける コンクリート・バネ理論: 例1 余白の大きさでレイアウトが崩れることはほぼない → 余白の拡縮で、表示領域の差分を吸収する 例)X, connpass, github, メディアサイトなど
1 可変な部分と不変な部分を切り分ける コンテンツの幅で吸収する コンテンツ幅に柔軟性がある場合、コンテンツの幅を変化させることで最大領域を利用できる 例)Figma, Visual Studio Codeなど
1 可変な部分と不変な部分を切り分ける コンクリート・バネ理論: 例2 コンテンツ幅に柔軟性がある場合、コンテンツの幅を変化させることで最大領域を利用できる 例)Figma, Visual Studio Codeなど
1 可変な部分と不変な部分を切り分ける 提供するコンテンツによって適切な選択肢が変わる 文字情報が中心 ユーザー操作が多い 読みやすい1行あたりの文字数 画面が大きいほど操作しやすい
2 コンポーネントレベルでのレスポンシブ ProductItem ポラーノの広場 購入する
2 コンポーネントレベルでのレスポンシブ ProductItem ポラーノの広場 A B 購入する C Q. 全体の横幅が小さくなったとき、どのスペースを削るか?
2 コンポーネントレベルでのレスポンシブ ポラーノの広場 購入する Aを小さくする ポラーノの広場 ポラーノ 購入する Bを小さくする 購入する Cを小さくする ポラーノの 広場 購入する Bを小さくする Q. 全体の横幅が小さくなったとき、どのスペースを削るか?
2 コンポーネントレベルでのレスポンシブ ProductItem ポラーノの広場 購入する 可変な部分 不変な部分
2 コンポーネントレベルでのレスポンシブ 可変な部分と不変な部分を切り分ける コンポーネントレベルで、サイズの変化をどこで吸収するかを考える 可変な部分が少ない方が、考えることを減らせる(できれば1要素) 多くの場合、テキストを含む要素が可変部分になりがち
2 コンポーネントレベルでのレスポンシブ 不変な部分はどこか? アイコン・ボタンなどのアトミックな要素。 サイズが変化すると操作性が落ちる(過剰に小さいボタンは操作に難あり) サイズが変化すると判別性が落ちる(小さすぎるアイコンは見えない) ポラーノの広場 購入する ポラーノの広場 購入する
2 コンポーネントレベルでのレスポンシブ テキスト部分でサイズを吸収する戦略 アイコン・ボタンなどのアトミックな要素は、縮むと見辛く操作しづらい 特にボタンなどは操作性に直結する Truncate(...で省略) 別の場所で全体を見ることができる場合など 改行を許容する ここでしかこのテキストを見れない場合 あのイーハトーヴォのすきと インストール あのイーハトーヴォのすきと おった風、夏でも底に冷たさを もつ青いそら、うつくしい森で インストール 飾られたモリーオ市、郊外のぎ らぎらひかる草の波。
2 コンポーネントレベルでのレスポンシブ テキスト部分でサイズを吸収する戦略 アイコン・ボタンなどのアトミックな要素は、縮むと見辛く操作しづらい 特にボタンなどは操作性に直結する 文字数の制限は解決策にはならない ケースも ポラーノの広場 ポラーノの インストール インストール
2 コンポーネントレベルでのレスポンシブ 余談: 文字の大きさも不変 文章 = 改行を増やす・省略するなどのスペースの削り方が可能なので可変部分として扱われやすい ※ どちらの戦略をとっても、文字自体の大きさが変わっているわけではない ポラー 購入する ポラーノ の広場 購入する
2 コンポーネントレベルでのレスポンシブ 実装 flexbox(またはgrid)レイアウトを利用するとシンプルに実装可能 不変な部分 = flex-shrink:0 / 可変な部分 = flex-grow: ProductItem ポラーノの広場 購入する ポラーノの広場 購入する
2 コンポーネントレベルでのレスポンシブ 実装 flexbox(またはgrid)レイアウトを利用するとシンプルに実装可能 不変な部分 = flex-shrink:0 / 可変な部分 = flex-grow: ProductItem ポラーノの広場 購入する ポラーノの広場 購入する
2 コンポーネントレベルでのレスポンシブ コンポーネントレベルでも同様に考える アイコンが縮小すると判別しづらくなる → 文章は改行が増えても可読性が(比較的)落ちない
2 コンポーネントレベルでのレスポンシブ コンポーネントレベルでも同様に考える アイコンが縮小すると判別しづらくなる → 文章は改行が増えても可読性が(比較的)落ちない
3 コンテナクエリ それでもレイアウトが崩れる場合に... コンテナークエリーの利用
3 コンテナクエリ 可変な部分と不変な部分をうまく制御したとしても、どうにもならない場合 → 次の手として、要素のレイアウトを変化させる = コンテナークエリー ポ ラ ー ノ の 広 場 ポラーノの広場 購入する 購入する
3 コンテナクエリ メディアクエリー コンテナークエリー ページ(ビューポート)のサイズ コンテナーのサイズ
コンテナクエリ コンテナークエリー そのコンポーネントの(親要素の)表示領域のサイズによってプロパティを出し分ける メディアクエリと異なり、特定のコンテナを指定してスコープを狭めることができるため、 コンポーネント志向と相性が良い
3 コンテナクエリ メディアクエリーじゃダメ?
3 コンテナクエリ こんなコンポーネントがあるとき... 幅が大きいとき 試し読み 幅が小さいとき 購入する 試し読み 購入する
3 コンテナクエリ メディアクエリーだとうまくいかない例: 縦長のバージョンで表示したいが、 ビューポートは大きいので、横長の バージョンが採用される ビューポートの大きさ 試し読み 購入する 試し読み 購入する 試し読み 購入する 試し読み 購入する 試し読み 購入する 試し読み 購入する 試し読み 購入する 試し読み 購入する ≠ コンポーネントの表示領域
3 コンテナクエリ メディアクエリーだとうまくいかない例: コンポーネント実装時に、そのコンポーネントの 利用シーンを考える必要がある 心の声: 1200px以上だと4カラムで表示されるから縦長で... 800px ~ 1200pxだと2カラムだから横長で... → コンポーネントなのに、ページ全体に関心を持たざる を得ない 試し読み 購入する
3 コンテナクエリ コンテナークエリー コンテナーの指定 コンテナークエリー の2つがセットになる。 コンテナーのサイズ
3 コンテナクエリ コンテナークエリー 基準とする要素にcontainerを指定 container: [名前] / [container-type] container-type: コンテナーコンテキストの種類 inline-size: インライン方向のサイズ size: インライン・ブロック方向のサイズ scroll-state: スクロール状態・snap・sticky normal
3 コンテナクエリ コンテナークエリー 実際に適用したいプロパティを@containerの 中に記述する。 コンテナは複数存在しうるので、コンテナ名を 指定できる @container [名前] ([条件]) { ... }
3 コンテナクエリ 余談: サイズ条件の指定 min-width: 400px のような指定だけでなく、不等号や論理演算子がサポートされており、 より直感的な記述が可能になっている。(メディアクエリーでも同様)
3 コンテナクエリ 表形式のUIなど、レイアウトを自由に変更できない場合は横スクロールで 対応することも考える。 ただし縦スクロールとは違い、操作性が落ちることは考える必要がある。 ホイールつきのマウスでは横スクロールができない(ショートカット キーの存在を知らない人も多い)
4 JSで処理を切り分ける それでもレイアウトが崩れる場合に... 可変な部分と、不変な部分を切り分ける コンテナークエリーの利用 JSで処理を切り分ける
4 JSで処理を切り分ける CSSではどうにもならない例: 画面幅によって呼び出すAPIを変える必要がある Canvasを利用してUIを描画している JavaScriptのAPIを利用する window.matchMedia
4 JSで処理を切り分ける window.matchMedia CSSのMedia Queryの条件と同じ記述を文字列で与えることができる。
4 JSで処理を切り分ける resizeイベントの利用は多くの場合 too much resizeイベントは、サイズが変更されるたびに発火する 特定の値を跨ぐ場合のみで十分な場合はmatchMediaの方が良い
4 JSで処理を切り分ける レスポンシブ対応の方針 可変な部分と不変な部分を切り分ける コンテナークエリーの利用 JSで処理を切り分ける
4 JSで処理を切り分ける シンプルな方法から対応する 指定した「コンテナ」に応じて、CSSを出し分ける。 メディアクエリと異なり、特定のコンテナを指定してスコープを狭めることができるため、 コンポーネント志向と相性が良い コンテンツのサイズで吸収 あのイーハトーヴォのすきとおった風 幅が大きい インストール コ ンテナクエリで調整 あのイーハトーヴォのすきと インストール おった風 あのイーハトーヴォ のすきとおった風 インストール 幅が小さい
5 まとめ 実装の複雑さ コンクリート・バネ理論 サイズ変化に対応できる余地を作る(flexbox やgrid) スコープに閉じたコンテナクエリーの利用 CSSの出しわけは発生するが、スコープに閉じ 込めることが可能 メディアクエリーの利用 JSレベルでの分岐 下に行くほど 実装の複雑度が増える
5 まとめ 実装の複雑さ 変化に柔軟なデザインを 採用する (flex, grid) コンポーネントレベルで のCSSの出しわけ (コンテナークエリー) ページレベルでのCSSの 出しわけ (メディアクエリー) JSでの出しわけ CSSレベルの分岐が存在し ない ○ × × × CSSレベルの分岐が特定の スコープに閉じている ○ ○ × × 実行中に、CSSが動的に変 更されない ○ ○ ○ × 特徴 メンテナンスしやすい スコープに閉じて、CSS の分岐がある ビューポートサイズに よって分岐がある CSSを動的に書き換え る 複雑
5 まとめ 実装の複雑さ シンプルな方法"だけ"で実現できるケースは少ない → シンプルな方法で管理できる"領域を増やす"
5 まとめ まとめ レスポンシブ対応のことを考えるときは、サイズの変化をどこで吸収 するべきかを考えよう flexboxやコンテナークエリーなどを使ってスコープを絞ろう
6 Appendix Clampとビューポート単位の利用 vw, vh, dvh などのビューポート単位を利用することで、画面サイズに応 じて滑らかに変化するデザインを利用できる
6 Appendix Clampとビューポート単位の利用 画面サイズによって逐次変化するため、全体でデザインの統一をとるのが難しい印象。 見出しやコピーテキストなどの、メインどころに限って入れるのが良いかもしれない。 (良い方法があるのかも) 最小値 最大値 ビューポートの6%のサイズを利用