响应式设计
选择合适的响应式边界,调整页面列数与可复用摘要,并验证布局发生变化的宽度。
本页内容
#概览
页面布局与可复用卡片可能需要不同的响应式规则。页面跟随视口,卡片则跟随父元素提供的空间。在 Master CSS 中,将条件附加到需要改变样式的 class 后方。
请先完成安装。这些示例使用默认预设,以及加载 @import '@master/css'; 的应用样式表。每个预览都提供观察边界所需的控件;生成的 CSS 显示随附 class 的输出。
#选择变化的依据
工作区列数这类页面整体决策,使用视口条件。同一组件可能出现在主列、侧栏或其他受限区域时,则使用容器查询。
| 由什么决定? | 写法 | 测量对象 |
|---|---|---|
| 页面视口 | grid-cols(3)@md | 视口宽度 |
| 组件可用空间 | grid-cols(2)@container(--md) | 最近且符合条件的祖先容器宽度 |
同一视口,两种组件宽度
根字号为 16px 时,28rem 等于 448px。这些是实际容器宽度:同一视口内,宽摘要分为双轨道,窄摘要保持堆叠。
文字说明
- 相同视口. 两张卡片位于同一页面。视口断点看到的宽度相同。
- 不同容器. 主容器宽 448px,侧栏容器宽 160px。
- 局部响应. 每个祖先使用 container-inline。后代卡片使用 grid-cols(1) 与 grid-cols(2)@container(--md),所以只有主列卡片达到预设的 28rem 查询阈值。
图中两张卡片共用同一个视口,但父元素宽度不同。视口条件会同时应用到两者;容器查询则让窄版保持堆叠,宽版分为多列。
#让页面随视口调整
从单列开始,在内容有足够空间时再增加列数。此工作流程使用预设的 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) 是基础布局。条件只替换列数;内容顺序、间距与表面样式保持不变。三列只有在内容放得下时才有帮助——采用阈值前,先在应用中试用较长标签。
@md 读取预设的 @custom-media --md 定义,不会读取 --breakpoint-md 主题变量。若要调整所有使用 @md 的 class 阈值,在应用 CSS 入口重新声明自定义媒体查询:
@import '@master/css';@custom-media --md (width >= 48rem);若新边界只属于某个布局,而非整个预设,请使用项目专属名称:
@custom-media --workspace-wide (width >= 72rem);<section class="grid-cols(1) grid-cols(3)@workspace-wide">…</section>断点参考列出预设名称与值。按内容需求选择阈值;md 这类名称不代表特定设备。
#让组件随容器调整
在祖先元素加上 container-inline,再为后代元素设置容器条件。此处摘要与状态在窄区域中上下堆叠;祖先达到预设 --md 容器阈值 28rem 时,改为两列。
摘要随容器调整
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 出现。控件受限于文章可用宽度;若文章本身比阈值窄,请加宽浏览器。
祖先建立查询边界,后代改变布局。元素不能把自己的尺寸当作容器查询的祖先。让卡片高度由内容决定,文本换行时才能自然增长。
#区分两套尺度
@md 与 @container(--md) 使用不同定义:
| Class 或值 | 在预设中的含义 |
|---|---|
@md | 自定义媒体查询的 64rem 视口阈值 |
@container(--md) | 自定义容器条件的 28rem 容器阈值 |
max-w-md | 使用 --container-md 尺寸 token 的最大宽度 |
调整 --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 } }}两个状态读数位于同一个窄内层容器中。加宽外层边界只会改变具名查询的结果。容器命名是选择祖先,不是让元素查询自身。
具名查询可越过较近容器
名称选择符合条件的祖先。被查询元素须置于该祖先内;容器不会查询自己。
文字说明
嵌套 article 与 card 容器边界包住同一个子元素。蓝色标示将子元素的 article 查询配对到外层具名 article 边界;较近的 card 边界仍为紫色。
#只对改变的样式加条件
共用样式保持无条件。只有需要不同值的属性与范围才加上条件。例如,侧栏在低于 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 定义时,也要重新声明补集;改变一个已编译定义不会使另一个重新展开。
改变布局,不重复表面样式
带条件的布局 class 保留其他无关声明。此示意图省略非必要的卡片内容。
文字说明
两张卡片共用表面、圆角与文本。父元素跨越阈值时,只有布局从堆叠改为横排。
#验证边界,不只看截图
测试每个阈值前一刻与刚好达到阈值的情况。页面示例请检查实际 iframe 视口的 599/600px 与 1023/1024px。组件示例则固定页面视口,以 16px 字号检查祖先元素的 447/448px。
结果不如预期时,检查后代元素计算后的 grid-template-columns。确认条件匹配、容器查询使用预期且符合条件的祖先,并且没有其他规则在层叠中胜出。
最后测试长文本、放大文本与窄布局。每种宽度都应保持合理的阅读顺序、允许内容换行,并避免固定卡片高度。布局会另外说明网格与间距的决策,不与查询边界混为一谈。







