订单摘要
组件内部只关心宿主的可用宽度。
如果一个卡片组件要根据“自己所在的列宽”改变布局,最稳妥的做法不是继续扩大媒体查询的条件,而是在组件宿主上声明 container-type,再让内部的 @container 只读取这个宿主的尺寸。通常只关心横向空间时使用 inline-size;只有在确实需要同时查询行内轴和块轴时,才使用 size。
容器查询的隔离边界由容器类型和祖先关系共同决定。未命名的 @container 会向祖先查找最近的合格查询容器;当页面里有多个相似的容器时,再用 container-name 给查询加一道名称过滤。这样组件被放进侧栏、主栏或弹窗时,内部规则仍然围绕自己的宿主工作。
先在组件宿主建立查询容器,再决定查询轴向;如果页面存在嵌套容器,就给真正承担布局职责的宿主命名。不要把整个页面壳层随手设成唯一的查询容器,否则组件很容易响应错误的祖先尺寸。

container-type 的作用是把元素声明为某一类查询容器。对于尺寸查询,常用值有三个:normal 是默认值,不建立尺寸查询容器;inline-size 只让查询读取行内轴;size 同时建立行内轴和块轴的尺寸约束。
以横向书写模式为例,行内轴通常对应水平方向,所以卡片组件多数只需要下面这样声明:
订单摘要
组件内部只关心宿主的可用宽度。
/* 只为这个组件宿主建立行内尺寸查询上下文 */
.card-host {
container-type: inline-size;
}
/* 默认状态先保证窄容器也能阅读 */
.card {
display: grid;
gap: 0.75rem;
grid-template-columns: 1fr;
}
/* 查询的是最近合格宿主,不是浏览器视口 */
@container (width >= 32rem) {
.card {
grid-template-columns: minmax(8rem, 0.8fr) minmax(0, 1.2fr);
}
}
这里的关键不是“把卡片变成响应式”,而是把响应式依据从视口转移到 .card-host。同一个 .card 放在宽主栏和窄侧栏中,会分别根据各自宿主的宽度选择规则。
inline-size 是组件布局的默认起点。它会为元素建立 style containment 和 inline-size containment,查询只针对行内轴。相比之下,size 同时作用于行内轴和块轴,适合确实需要查询高度、宽度、方向或纵横比的布局。
| 值 | 适合的问题 | 需要留意的边界 |
|---|---|---|
normal | 只需要名称查询或样式查询 | 不能作为尺寸查询的容器 |
inline-size | 卡片列宽、列表项宽度、横向布局切换 | 组件宿主必须有可计算的行内尺寸 |
size | 同时根据宽高、方向或纵横比调整布局 | 尺寸隔离后不能再依赖子内容自动撑出容器尺寸 |
最容易踩坑的是直接把所有组件都换成 size。尺寸 containment 会让容器在计算尺寸时忽略子内容;如果外层没有明确宽高、网格轨道或可拉伸的上下文,容器可能出现尺寸塌缩。只按宽度切换时,用 inline-size 可以把这类约束控制在更小的范围。
/* 只有查询宽高比时,才让两个轴都进入容器查询上下文 */
.media-panel {
container-type: size;
aspect-ratio: 4 / 3;
}
/* 宽高条件同时成立时调整内容排列 */
@container (min-width: 36rem) and (min-height: 18rem) {
.media-panel .meta {
display: grid;
grid-template-columns: 1fr auto;
}
}
写出不带名字的 @container (width >= 32rem) 后,浏览器会从被设置样式的元素向外寻找合格的查询容器。命中的是最近的符合条件的容器,而不是页面中任意一个更宽的外层元素。因此,查询边界最好放在组件真正拥有布局责任的宿主上。
例如下面有一个页面壳层和一个卡片宿主。卡片内容的查询应该读取 .card-host,不要把壳层当成卡片的隐式尺寸来源:
/* 页面壳层可以控制整体布局,但不必成为卡片尺寸查询容器 */
.page-shell {
display: grid;
grid-template-columns: minmax(0, 2fr) minmax(14rem, 1fr);
}
/* 卡片宿主才是组件查询的边界 */
.card-host {
container-type: inline-size;
}
/* 该规则只会在最近合格宿主满足条件时生效 */
@container (width >= 32rem) {
.card__body {
padding: 1.25rem;
}
}
嵌套容器也遵守这个思路。若卡片内部再放一个独立的表格容器,表格的规则会优先考虑更近的表格宿主。这样可以让外层卡片决定整体排布,内层表格决定自己的列布局,两者不会共享一个含义模糊的“全局宽度”。
当一个组件树里存在多个候选查询容器时,给容器命名能把意图写进 CSS。显式写法是同时设置 container-name 和 container-type,也可以使用 container 简写:
/* 给承担卡片宽度职责的宿主取一个局部、可读的名字 */
.card-host {
container-name: card;
container-type: inline-size;
}
/* 名称过滤会排除没有 card 名称的其它容器 */
@container card (width >= 32rem) {
.card {
grid-template-columns: 12rem minmax(0, 1fr);
}
}
/* 等价的简写:名称在斜杠左侧,类型在右侧 */
.table-host {
container: data-table / inline-size;
}
命名并不会让容器脱离祖先树,也不会把名称变成选择器。它做的是在容器查询筛选阶段缩小候选集合。名称应表达“这个容器承担什么布局职责”,例如 card、data-table,而不是写成容易随页面层级变化的 level-3。

检查宿主是否是 inline-size 或 size。如果仍是默认的 normal,尺寸条件没有可供读取的容器上下文。只使用 container-name 不能替代尺寸容器类型。
检查网格轨道、弹性布局、明确宽度或块级拉伸是否真的给宿主提供了尺寸。尤其是 size 可能让容器不再依赖子内容撑开;外层没有尺寸约束时,查询条件自然不会按预期变化。
横向书写模式下二者常常看起来一致,但 inline-size 是逻辑轴。如果项目改变了 writing-mode,按逻辑轴表达的容器查询比写死物理方向更容易保持正确。排查时要把书写模式和查询特征放在一起看。
未命名查询可能命中了更近的嵌套容器;命名查询则可能因为 container-name 拼写、作用域或容器类型不匹配而没有候选。把真正负责布局的宿主命名,并让内部规则只使用这个名字,通常比提高选择器权重更有效。
我在整理可复用组件时,会把“谁提供尺寸、谁读取尺寸”当成组件接口来设计。宿主负责建立 container-type,组件内部只写自己的 @container 条件;页面壳层只负责把宿主放进合适的网格或弹性空间。这个分工比让组件猜视口状态更容易复用。
可以用下面三条规则做代码审查:
inline-size,不要为了“更完整”直接使用 size。container-name。它不能作为尺寸查询容器,但元素仍可能参与名称查询或样式查询。若条件涉及 width、height、orientation 等尺寸特征,应改用 inline-size 或 size。
页面只有一个清晰的查询宿主时,未命名查询通常足够;有嵌套容器或多个相似布局区域时,用名称可以把查询意图固定在指定职责上。
因为 size 同时引入块轴尺寸 containment,容器计算尺寸时不能简单依赖子内容。给宿主明确的高度、纵横比、网格轨道或其它外部尺寸上下文,并确认这正是你要查询的维度。
总结一下:container-type 决定组件能查询哪条尺寸轴,最近合格祖先决定未命名查询读谁,container-name 负责在复杂嵌套里缩小候选。先把这三层边界设计清楚,容器查询就能从“样式偶尔误触发”变成可维护的组件布局接口。
前端 PerformanceObserver 怎样接收长任务条目