レスポンシブデザイン
適切なレスポンシブの境界を選び、ページの列や再利用する概要を調整し、レイアウトが変わる幅を検証します。
このページの内容
#概要
ページのレイアウトと再利用するカードでは、必要なレスポンシブルールが異なる場合があります。ページはビューポートに、カードは親から与えられた空間に合わせます。Master CSS では、スタイルを変えたいクラスに条件を付けます。
まずインストールを済ませてください。ここでは標準プリセットと、@import '@master/css'; を読み込むアプリのスタイルシートを使います。各プレビューには境界を観察するための操作部品があります。生成 CSS で、添えたクラスの出力を確認できます。
#変化の基準を選ぶ
ワークスペースの列数など、ページ全体の判断にはビューポート条件を使います。同じコンポーネントがメイン列、サイドバー、幅の限られた領域に置かれる場合はコンテナークエリーを使います。
| 何を基準にするか | 書き方 | 測定対象 |
|---|---|---|
| ページのビューポート | grid-cols(3)@md | ビューポートの幅 |
| コンポーネントが使える空間 | grid-cols(2)@container(--md) | 条件を満たす最も近い祖先コンテナーの幅 |
1つのビューポート、2つのコンポーネント幅
ルートが 16px なら 28rem は 448px です。これは実際のコンテナー幅です。同じビューポート内で、広い概要は2トラック、狭い概要は縦並びになります。
テキストによる説明
- 同じビューポート. 2枚のカードは同じページにあります。ビューポートのブレークポイントでは同じ幅として扱われます。
- 異なるコンテナー. メインのコンテナーは幅 448px、サイドバーは幅 160px です。
- 局所的な応答. 各祖先は container-inline を使います。子孫のカードは grid-cols(1) と grid-cols(2)@container(--md) を使い、メインのカードだけがプリセットの 28rem に達します。
図の2枚のカードは同じビューポート内にありますが、親の幅が異なります。ビューポート条件は両方に同時に一致します。コンテナークエリーなら、狭い方は縦並びを保ち、広い方だけ列に分けられます。
#ページをビューポートに合わせる
1列から始め、内容に余裕ができるところで列を増やします。このワークフローは、プリセットの 2xs の 37.5rem と md の 64rem を使います。標準の初期文字サイズ 16px では、600px と 1024px に相当します。
ページの列をビューポートに合わせる
<section aria-label="Publishing workflow" class="grid-cols(1) gap-md grid-cols(2)@2xs grid-cols(3)@md"> <article class="p-md r-lg b(1) b-solid b-line-divider bg-surface-raised"> <h3 class="m(0) text-sm font-regular fg-text-strong">Draft</h3> <p class="mb(0) mt-xs text-sm fg-text-muted">Collect the changes.</p> </article> <article class="p-md r-lg b(1) b-solid b-line-divider bg-surface-raised"> <h3 class="m(0) text-sm font-regular fg-text-strong">Review</h3> <p class="mb(0) mt-xs text-sm fg-text-muted">Check the examples.</p> </article> <article class="p-md r-lg b(1) b-solid b-line-divider bg-surface-raised"> <h3 class="m(0) text-sm font-regular fg-text-strong">Publish</h3> <p class="mb(0) mt-xs text-sm fg-text-muted">Share the release notes.</p> </article></section>生成された CSS
@layer theme { :root, :host { --color-zinc-40: oklch(68.6% .0028 265); --color-zinc-60: oklch(48.6% .003 265); --color-zinc-90: oklch(24.6% .0022 265); --color-zinc-100: oklch(15% .0016 265) } :root, :host { --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90)); --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12)); --color-text-strong: light-dark(var(--color-zinc-100), var(--color-white)); --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40)) } :root, :host { --color-white: oklch(100% 0 none) } :root, :host { --font-weight-regular: 400; --font-size-sm: .875rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --radius-lg: .5rem; --spacing-xs: .5rem; --spacing-md: 1rem }}@layer utilities { .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .grid-cols\(1\) { display: grid; grid-template-columns: repeat(1, minmax(0, 1fr)) } .bg-surface-raised { background-color: var(--color-surface-raised) } .b-line-divider { border-color: var(--color-line-divider) } .r-lg { border-radius: var(--radius-lg) } .b-solid { border-style: solid } .b\(1\) { border-width: calc(1 * 1px) } .fg-text-muted { color: var(--color-text-muted) } .fg-text-strong { color: var(--color-text-strong) } .font-regular { font-weight: var(--font-weight-regular) } .gap-md { gap: var(--spacing-md) } .m\(0\) { margin: calc(0 * 1px) } .mt-xs { margin-top: var(--spacing-xs) } .mb\(0\) { margin-bottom: calc(0 * 1px) } .p-md { padding: var(--spacing-md) } @media (width >= 37.5rem) { .grid-cols\(2\)\@2xs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) } } @media (width >= 64rem) { .grid-cols\(3\)\@md { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)) } }}grid-cols(1) が基本のレイアウトです。条件が置き換えるのは列数だけで、内容の順序、間隔、面のスタイルは変わりません。3列が役立つのは内容が収まる間だけです。アプリでしきい値を採用する前に、長いラベルで試してください。
@md はプリセットの @custom-media --md 定義を読みます。テーマ変数の --breakpoint-md は読みません。@md を使うすべてのクラスのしきい値を変えるには、アプリの CSS エントリーでカスタムメディアを再宣言します。
@import '@master/css';@custom-media --md (width >= 48rem);新しい境界がプリセット全体ではなく1つのレイアウト用なら、プロジェクト固有の名前を使います。
@custom-media --workspace-wide (width >= 72rem);<section class="grid-cols(1) grid-cols(3)@workspace-wide">…</section>ブレークポイントのリファレンスに、プリセットの名前と値を示しています。しきい値は内容の必要に応じて選びます。md などの名前は特定の端末を指しません。
#コンポーネントをコンテナーに合わせる
祖先に container-inline を付け、子孫にコンテナー条件を指定します。ここでは狭い領域で概要とステータスを縦に並べ、祖先がプリセットの --md コンテナー条件の 28rem に達すると2列にします。
コンテナーに合わせて変わる概要
Release notes
Review the changes before publishing.
- Status
- Draft ready
- Next step
- Editorial review
<section class="container-inline"> <article class="grid-cols(1) gap-lg p-md b-solid b(1) b-line-divider r-lg bg-surface-raised grid-cols(2)@container(--md)"> <div class="min-w(0)"> <h3 class="m(0) text-md font-regular fg-text-strong">Release notes</h3> <p class="mb(0) mt-xs text-sm fg-text-muted">Review the changes before publishing.</p> </div> <dl class="grid gap-xs m(0) text-sm"> <div> <dt class="fg-text-muted">Status</dt> <dd class="m(0) fg-text-strong">Draft ready</dd> </div> <div> <dt class="fg-text-muted">Next step</dt> <dd class="m(0) fg-text-strong">Editorial review</dd> </div> </dl> </article></section>生成された CSS
@layer utilities { .grid-cols\(1\) { display: grid; grid-template-columns: repeat(1, minmax(0, 1fr)) } .container-inline { container-type: inline-size } @container (width >= 28rem) { .grid-cols\(2\)\@container\(--md\) { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)) } }}ブラウザーウィンドウを固定したまま コンテナー スライダーを動かします。変わるのは祖先のサイズで、ビューポートではありません。文字サイズ 16px で 447px と 448px を比べると、448px で2列目が現れるはずです。操作部品は記事の幅内に収まるため、記事自体がしきい値より狭ければブラウザーを広げてください。
祖先がクエリーの境界となり、子孫がレイアウトを変えます。要素は自分のサイズを、自身のコンテナークエリーの祖先として使えません。カードの高さは内容に任せ、折り返した文字に合わせて伸びるようにします。
#2つの尺度を区別する
@md と @container(--md) は異なる定義を使います。
| クラスまたは値 | プリセットでの意味 |
|---|---|
@md | カスタムメディアによる 64rem のビューポートしきい値 |
@container(--md) | カスタムコンテナー条件による 28rem のコンテナーしきい値 |
max-w-md | サイズトークン --container-md を使う最大幅 |
--container-md を変えると幅の上限が変わりますが、どちらのクエリーのしきい値も変わりません。再利用する条件は @custom-container で定義し、局所的な条件なら grid-cols(2)@container(width>=30rem) のように書けます。構文と全スケールは名前付きコンテナー条件を参照してください。
#外側のコンテナーに名前を付ける
名前のないサイズクエリーは、条件を満たす最も近い祖先を測定します。入れ子のレイアウトに別のコンテナーができると、内側の境界が使われます。外側の幅を測りたい場合は、(container-name:outer) で名前を付けて @container(outer|--md) を使います。
測定する祖先を選ぶ
<section class="(container-name:outer) container-inline p-md r-lg b(1) b-solid b-line-divider"> <p class="mt(0) text-sm fg-text-muted">Outer container</p> <div class="container-inline (max-width:14rem)"> <p class="text-sm fg-text-muted">Inner container · at most 14rem</p> <div class="p-md r-md bg-line-subtle b-line-divider b(1) b-solid b-text-blue@container(--md)"> <h3 class="m(0) text-sm font-regular">Nearest container</h3> <p class="mb(0) mt-xs text-sm"> <span class="hidden@container(--md)">Below 28rem</span> <span class="hidden inline@container(--md)">At least 28rem</span> </p> </div> <div class="mt-md p-md r-md bg-line-subtle b-line-divider b(1) b-solid b-text-blue@container(outer|--md)"> <h3 class="m(0) text-sm font-regular">Named outer container</h3> <p class="mb(0) mt-xs text-sm"> <span class="hidden@container(outer|--md)">Below 28rem</span> <span class="hidden inline@container(outer|--md)">At least 28rem</span> </p> </div> </div></section>生成された CSS
@layer theme { :root, :host { --color-zinc-40: oklch(68.6% .0028 265); --color-zinc-60: oklch(48.6% .003 265); --color-blue-30: oklch(72.87% .1468 249.3); --color-blue-60: oklch(51.83% .2687 266.1) } :root, :host { --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12)); --color-line-subtle: light-dark(oklch(0% 0 none / .06), oklch(100% 0 none / .06)); --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40)); --color-text-blue: light-dark(var(--color-blue-60), var(--color-blue-30)) } :root, :host { --font-weight-regular: 400; --font-size-sm: .875rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --radius-md: .375rem; --radius-lg: .5rem; --spacing-xs: .5rem; --spacing-md: 1rem }}@layer utilities { .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .bg-line-subtle { background-color: var(--color-line-subtle) } .b-line-divider { border-color: var(--color-line-divider) } .r-lg { border-radius: var(--radius-lg) } .r-md { border-radius: var(--radius-md) } .b-solid { border-style: solid } .b\(1\) { border-width: calc(1 * 1px) } .fg-text-muted { color: var(--color-text-muted) } .container-inline { container-type: inline-size } .hidden { display: none } .font-regular { font-weight: var(--font-weight-regular) } .m\(0\) { margin: calc(0 * 1px) } .mt-md { margin-top: var(--spacing-md) } .mt-xs { margin-top: var(--spacing-xs) } .mb\(0\) { margin-bottom: calc(0 * 1px) } .mt\(0\) { margin-top: calc(0 * 1px) } .p-md { padding: var(--spacing-md) } .\(container-name\:outer\) { container-name: outer } .\(max-width\:14rem\) { max-width: 14rem } @container (width >= 28rem) { .b-text-blue\@container\(--md\) { border-color: var(--color-text-blue) } } @container (width >= 28rem) { .inline\@container\(--md\) { display: inline } } @container (width >= 28rem) { .hidden\@container\(--md\) { display: none } } @container outer (width >= 28rem) { .b-text-blue\@container\(outer\|--md\) { border-color: var(--color-text-blue) } } @container outer (width >= 28rem) { .inline\@container\(outer\|--md\) { display: inline } } @container outer (width >= 28rem) { .hidden\@container\(outer\|--md\) { display: none } }}2つのステータス表示は、同じ狭い内側コンテナーの中にあります。外側を広げても、変わるのは名前付きクエリーの結果だけです。コンテナーの命名は祖先を選ぶもので、要素が自分自身を問い合わせる仕組みではありません。
名前付きクエリーは近いコンテナーを越えて選べる
名前で適格な祖先を選びます。対象の要素はその祖先内に置いてください。コンテナーは自分自身を問い合わせません。
テキストによる説明
入れ子の article と card のコンテナー境界が1つの子を囲んでいます。青い印が子の article クエリーと外側の名前付き article 境界を対応付け、近い card 境界は紫のままです。
#変わるスタイルだけに条件を付ける
共通のスタイルには条件を付けません。値を変える必要のあるプロパティと範囲だけに条件を加えます。たとえば aside に md 未満で浮き上がった面を使い、大きなレイアウトにはその背景を持ち越さないようにできます。
<aside class="p-md bg-surface-raised@max-md">…</aside>生成された CSS
@layer theme { :root, :host { --color-zinc-90: oklch(24.6% .0022 265) } :root, :host { --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90)) } :root, :host { --color-white: oklch(100% 0 none) }}@layer utilities { @media not (width >= 64rem) { .bg-surface-raised\@max-md { background-color: var(--color-surface-raised) } }}@max-md はプリセットの @md の補集合で、両者は重なりません。--md の再宣言で両方が移動します。hidden@media(52.125rem<=width<=64rem) のような局所的な範囲条件なら、プロジェクトの名前付きしきい値を変えずに特定区間を指定できます。構文は条件のリファレンスを参照してください。
最小条件と補集合は重ならず接する
点は境界を含むかどうかを示します。軸はプリセットの md しきい値付近だけを表し、デバイスサイズ表ではありません。
テキストによる説明
- プリセットの @md は width >= 64rem から始まります。@max-md は補集合で、そのしきい値自体は含みません。
- 63.999rem では max-md だけ、64rem では md だけが一致します。境界に隙間や重なりはありません。
- 展開済み manifest 定義を置き換える場合は補集合も再宣言します。片方のコンパイル済み定義を変えても、もう片方は再展開しません。
面を指定し直さずにレイアウトを変える
条件付きのレイアウトクラスは、無関係な宣言をそのまま保ちます。この模式図では不要なカード内容を省略しています。
テキストによる説明
2枚のカードは面、角丸、文字を共有しています。親がしきい値を越えると、レイアウトだけが縦並びから横並びに変わります。
#スクリーンショットだけでなく境界を検証する
各しきい値の直前と、ちょうどその値を試します。ページの例では実際の iframe ビューポートを 599/600px と 1023/1024px で確認します。コンポーネントの例ではページのビューポートを固定し、文字サイズ 16px で祖先を 447/448px にして確認します。
結果が想定と違う場合は、子孫の計算後の grid-template-columns を調べます。条件が一致しているか、コンテナークエリーが意図した適格な祖先を使っているか、カスケードで別のルールが勝っていないかを確認します。
最後に長い文章、拡大した文字、狭いレイアウトを試します。どの幅でも読み順を保ち、折り返しを許可し、カードの高さを固定しないようにします。レイアウトでは、クエリーの境界とは別にグリッドと間隔の選び方を扱います。







