コンテンツにスキップ

第4章 レスポンシブ設計

画面サイズによる変化も、どの境界の構造が変化するかによって責務を決めます。

「レスポンシブ対応をどこに書くか」は、CSS のメディアクエリをどのファイルに置くかという話ではなく、どの単位がその変化を知るべきかという話です。

ひとつの Island の内部だけでレイアウトが変化する場合、その Island 自身が責務を持ちます。

たとえばカンバンが、利用可能な横幅に応じて列の配置を変更するとします。

Wide
KanbanIsland
[ Todo ][ Doing ][ Done ]
Narrow
KanbanIsland
[ Todo ]
[ Doing ]
[ Done ]

機能そのものや、他の Island との関係は変わっていません。変化しているのは KanbanIsland 内部の表現だけなので、Scaffold がその詳細を知る必要はありません。

Island は、自身に与えられた利用可能領域に応じて内部構造を適応できます。ここで見るのは画面全体の幅ではなく、自分に割り当てられた幅です。同じカンバンが、広い画面のサイドパネルに置かれることも、狭い画面の全幅に置かれることもあります。画面幅で判断すると、この2つを区別できません。

「自分に割り当てられた幅」を知る仕組みは、どの環境にも用意されています。CSS のコンテナクエリ、Flutter の LayoutBuilderResizeObserver などがそれにあたります。名前と使い方は環境ごとに違いますが、親から与えられたサイズを測って分岐するという点は共通です。

画面幅を見るメディアクエリしか使ったことがない場合、ここが切り替えの分かれ目になります。メディアクエリは Scaffold の道具、コンテナ単位の測定は Island の道具、と対応させると迷いません。

複数の Island の位置、表示方法、可視性などが変化する場合は、Scaffold が責務を持ちます。

デスクトップでは、タイムラインとスレッドを横に並べ、右端にトレンドを置きます。

Desktop
TimelineScaffold
┌────────────────┬──────────────┬──────────────┐
│ TimelineIsland │ ThreadIsland │ TrendsIsland │
└────────────────┴──────────────┴──────────────┘

モバイルでは、タイムラインだけを表示し、スレッドは別の画面へ切り替えます。トレンドは表示しません。

Mobile
TimelineScaffold
┌─────────────────────┐
│ TimelineIsland │
└─────────────────────┘
ThreadIsland → Page / Sheet
TrendsIsland → 表示しない

ここでは2種類の変化が起きています。ThreadIsland は表示される場所が変わり、TrendsIsland はそもそも表示されなくなりました。 どちらも Scaffold の判断です。

Island を表示するかどうかは、その Island 自身では決められません。TrendsIsland に「画面が狭いときは自分を描かない」と書くと、狭い領域に置きたい別の画面へ持っていったときに、何も表示されない Island になります。自分が要るかどうかを自分で判断させない、というのがここでの分かれ目です。

いずれの場合も、ThreadIslandTrendsIsland 自身の機能は変わっていません。1件の投稿と返信を見せる責務も、内部の状態も同じです。

変化しているのは、Island が画面上でどのように構成されるかです。そのため Scaffold が判断します。

ThreadIsland の内部に「自分がページとして開かれているのか、横並びの中央にいるのか」を判断するコードを書くと、その Island は画面のレイアウト方針に依存します。別の画面へ移したときに、その判断が意味を失います。

Island内部の変化
→ Island
Island同士の空間的な関係の変化
→ Scaffold

Bridge はレスポンシブレイアウトを扱わない

Section titled “Bridge はレスポンシブレイアウトを扱わない”

Bridge は論理的な協調境界なので、画面サイズに応じた配置変更は担当しません。

PostSelectionBridge
├─ TimelineIsland
└─ ThreadIsland

という関係は、デスクトップでもモバイルでも維持できます。

デスクトップでは2つの Island が並んでいて、

[ Timeline ][ Thread ]

モバイルでは別々の画面に表示されていても、

[ Timeline ]
Thread → Page

SelectedPostId を共有するという論理的な関係は同じです。

つまり、

Bridge は論理的な関係を構成し、Scaffold は空間的な関係を構成する。

レスポンシブによって描画構造が変化しても、機能間の関係まで変更する必要はありません。

この性質には実装上の利点があります。BridgeScaffold の内側で Island を包む位置にいれば、Scaffold がデスクトップ用とモバイル用のどちらのレイアウトを選んでも、Island は同じ Bridge から値を受け取ります。レイアウトを切り替えたときに SelectedPostId が失われる、という問題が起きません。

この要素の内部だけが変わるか?
→ その要素自身
複数Islandの配置関係が変わるか?
→ Scaffold
Island同士の論理関係が変わるか?
→ Bridge

3つ目に当てはまるケースはまれです。画面幅によって「共有していた状態を共有しなくなる」ことは、通常はありません。もし当てはまる場合は、それはレスポンシブ対応ではなく、画面幅ごとに別の機能を提供している可能性があります。