季度报告
这里是卡片摘要。
来源:17golang原创
时间:2026-10-09 12:29:59 120浏览 收藏
CSS 容器样式查询可以让后代组件读取父级的“计算后状态”,最稳妥的生产写法是:父级用自定义属性发布状态,组件用 @container style() 查询这个令牌,再改变自己的布局或视觉。这样组件不必知道父级用了哪个业务类名,也不用 JavaScript 把同一状态逐层复制到每个子元素。
当前应优先查询自定义属性,例如@container card style(--card-state: selected)。仅做样式查询不需要设置container-type;给父级设置container-name,则能避免组件意外查询到更近或更远的其他祖先。
直接给组件添加 .is-selected 当然能工作。容器样式查询真正有价值的场景,是父级掌握上下文,而组件只应该依赖一个稳定契约:
可以把它看成一种很小的“上下文协议”:父级负责把业务状态转换成 CSS 自定义属性,组件负责解释令牌。父级结构以后改变,只要令牌名称和取值不变,组件规则就不需要重写。
先准备一个宿主和一个真正需要改变的后代组件。注意 .card 必须位于查询容器内部,因为 @container 的规则作用于容器的后代,而不是把容器自身重新选中。
季度报告
这里是卡片摘要。
接着把业务属性转换成状态令牌,并给宿主命名。对于纯样式查询,默认的 container-type: normal 已经允许元素作为样式查询容器,因此这里只设置名称:
/* 默认状态令牌放在父级宿主,名称用于锁定查询祖先 */
.card-host {
container-name: card;
--card-state: default;
}
/* 业务状态只在宿主层转换,不向子组件复制类名 */
.card-host[data-state="selected"] {
--card-state: selected;
}
/* 基础样式始终生效,也是旧浏览器的回退 */
.card {
border: 1px solid #d7dce5;
border-radius: 12px;
padding: 16px;
background: #fff;
}
/* 只有父级计算值为 selected 时,后代组件才进入强调状态 */
@container card style(--card-state: selected) {
.card {
border-color: #4f46e5;
box-shadow: 0 8px 24px rgb(79 70 229 / 0.16);
}
.card__actions {
display: flex;
justify-content: flex-end;
}
}
这段代码里有三个独立责任:
data-state 表达业务状态。--card-state 是父级与组件之间的样式契约。@container card style(...) 只负责根据契约选择后代样式。
样式查询默认会从目标元素的祖先中选择符合条件的查询容器。由于所有元素默认都能成为样式查询容器,组件一旦嵌套进更复杂的结构,未命名查询可能读取到离它最近的另一个祖先。
下面两条规则含义不同:
/* 未命名:匹配后代元素最近的合格样式容器 */
@container style(--density: compact) {
.toolbar { gap: 4px; }
}
/* 命名:只在名为 workspace 的祖先中判断状态 */
@container workspace style(--density: compact) {
.toolbar { gap: 4px; }
}
组件库中更推荐第二种。容器名称不是为了改变状态,而是为了确定“向哪个祖先提问”。名称区分大小写,项目里应统一命名方式,避免页面层和组件层复用含义不同的名字。
规范允许样式查询描述普通 CSS 声明,但截至当前,跨浏览器可用的核心仍是自定义属性查询。像下面这种写法不能当作通用生产方案:
/* 普通属性 style() 查询目前不能作为通用兼容写法 */
@container style(background-color: black) {
.card { color: white; }
}
把状态显式放进 --theme: dark、--density: compact 或 --card-state: selected 更可靠,也能避免从颜色、边框等表现反推业务含义。
自定义属性值比较还需要注意令牌形式。未注册属性通常按计算后的令牌进行匹配,语义相近的两种写法不一定等价。例如颜色写成 blue 与 #0000ff,不应默认它们一定匹配同一条查询。状态值使用 selected、compact 这类稳定关键字,会比把颜色本身当状态更清楚。
容器样式查询的自定义属性支持已在 2026 年成为 Baseline Newly available,但真实项目仍可能面对旧浏览器、内嵌 WebView 或企业延迟升级环境。最简单的兼容策略不是写一套复杂检测,而是遵守渐进增强:
@container style()。如果父级状态会影响“按钮是否存在”或“内容是否可访问”,就不应该只靠 CSS 样式查询。业务可用性仍由 HTML 与应用状态控制,CSS 只负责表达视觉和布局。
样式查询和尺寸查询是两类条件。只查询自定义属性时,不需要声明 container-type;若还要根据父级宽度改变布局,就需要为尺寸查询建立容器:
/* 同时支持尺寸条件和状态条件,因此声明 inline-size */
.card-host {
container: card / inline-size;
--card-state: default;
}
/* 父级足够宽且处于 selected 状态时才切换双列布局 */
@container card (inline-size > 32rem) and style(--card-state: selected) {
.card {
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
}
}
这里的 inline-size 是尺寸查询的前提,不是 style() 的前提。把两者混为一谈,会导致开发者在所有父级上无意义地增加尺寸包含设置。

@container 为后代元素选择查询容器。若你只想根据元素自己的 data-state 改它自身,直接写属性选择器更简单:
/* 元素根据自己的属性改自身,不需要容器查询 */
.card-host[data-state="selected"] {
outline: 2px solid #4f46e5;
}
若父级和子组件由同一模板控制,添加一个语义类通常更直白。样式查询适合跨层级、跨组件或跨宿主传递上下文,不是为了消灭所有类选择器。
--card-state: selected 表示状态,--card-accent: #4f46e5 表示设计值。两者分开后,主题换色不会改变状态查询,业务状态变化也不会绑死某个颜色。
| 判断项 | 适合样式查询 | 更简单的替代 |
|---|---|---|
| 状态由祖先提供 | 是,使用自定义属性契约 | 同一元素状态用类或属性选择器 |
| 组件会放进多个宿主 | 是,命名容器隔离上下文 | 固定页面结构可直接写组合选择器 |
| 需要改变容器自身 | 通常不适合 | 类、属性选择器或 :has() |
| 需要查询普通 CSS 属性 | 暂不作为通用生产方案 | 显式发布自定义属性 |
| 还要查询父级尺寸 | 可以组合使用 | 为尺寸条件设置 container-type |
不需要,而且规范中没有 container-type: style。元素默认可作为样式查询容器;只有尺寸查询或滚动状态查询需要相应的 container-type。
未命名查询会选择目标后代的合格祖先。给预期父级设置 container-name,并在 @container 中写出名称,可以收窄选择范围。
规范描述了普通属性查询,但目前不能把它视为跨浏览器通用能力。生产代码优先让父级发布 --theme 或 --surface-state 这样的自定义属性。
如果 JavaScript 或 CSS 改变了父级自定义属性的计算值,浏览器会重新评估对应容器查询。应用只需要维护真实业务状态,不必再手动给每个后代同步类名。
CSS 容器样式查询最值得采用的地方,是把“父级上下文”变成组件可消费的样式契约。父级发布自定义属性,命名容器确定查询对象,后代组件在 @container style() 中响应状态;基础样式则留在查询外作为回退。
它不是类选择器的全面替代,也不适合控制业务可用性。只要守住后代选择、自定义属性支持、容器命名和渐进增强四个边界,这个模式就能让组件从具体页面结构中解耦,同时保持 CSS 规则可读。
参考资料:MDN《Using container size and style queries》、CSS Conditional Rules Module Level 5,以及 web.dev 2026 年 5 月 Web 平台更新。