容器样式查询是啥,怎么动手搞起来,小白不踩坑?

2,767字
12–18 分钟
in

前端圈最近又整出新活儿了,CSS容器样式查询(Container Style Queries)这玩意儿还在超级早期的阶段,但已经能尝鲜了。Chromium早就支持,Safari从16版本也开始跟进,Firefox估计也快了。说白了,以前搞响应式只能盯着浏览器窗口宽度(视口)做文章,现在能直接看某个容器自身的样式属性来改变内部元素的长相,这波操作杠杠的。

目录

概念整明白

容器样式查询,就是让一个容器把自己的样式值(比如背景色、字体风格)当成“检测开关”。当这个容器的某个CSS属性匹配上特定数值时,里头的子元素就自动换一套样式。打个比方:好比一个盒子穿上了橙色外套,盒子里的文字立马自动变成白色,不用手动加类名,也不用JS监听。这跟传统媒体查询不一样,媒体查询是看整个屏幕宽度,而样式查询是看身边那个老爹容器的样式。

实操走一波

想玩转这玩意儿,得先搭个测试环境。目前最稳当的是用Chrome Canary或者Safari Technology Preview,把“Experimental Web Platform Features”开关打开。下面一步步搞个能跑的例子。

第一步:定义一个容器

给外层元素起个名字,顺便声明它的查询类型。注意,按照最新的规范草案,样式查询其实不用特意写container-type: style,因为所有容器查询默认就支持样式查询。但为了代码清晰,可以写上。

/* 定义一个叫“card-box”的容器 */
.card-container {
  container-name: card-box;
  /* 下面这行可写可不写,写了更明白 */
  container-type: style;
}

第二步:容器本身带上某个样式

比如给这个容器来个橙色背景。这时候容器身上的background-color就成了一个“检测点”。

.card-container {
  background-color: #f8a100;
}

第三步:写样式查询规则

@container后面跟上容器名字和要查询的样式条件。当card-box这个容器的背景色正好是#f8a100时,里面的.card文字颜色变成白色。

@container card-box (background-color: #f8a100) {
  .card {
    color: #fff;
  }
}

完整代码放一块儿:

<div class="card-container">
  <div class="card">
    这里是一段文字,当外层容器背景变橙色时,文字自动白起来。
  </div>
</div>
.card-container {
  container-name: card-box;
  background-color: #f8a100;
  padding: 20px;
}

@container card-box (background-color: #f8a100) {
  .card {
    color: #fff;
    font-weight: bold;
  }
}

运行一下,文字会变成白色。如果手动把.card-container的背景色改成别的颜色,比如#333,那个查询条件不满足,白色样式就失效了。这个过程完全自动,不用写一行JS。

写代码时有个小坑:样式查询里的属性值必须和容器上计算后的实际值完全匹配。比如background-color: #f8a100写成background-color: orange,浏览器可能不认,因为计算后的值往往是rgb格式。稳妥的做法是查一下开发者工具里Computed那一栏的精确值。

方案举个栗子

样式查询不止能玩背景色,还能拿自定义属性(CSS变量)当状态机,或者解决嵌套斜体的尴尬场面。下面整两个实际能用的方案。

方案A:自定义属性驱动卡片模式

有时候想根据一个“模式开关”来改变卡片内部的布局。不用加额外类名,改个变量值就行。

/* 定义容器,并给一个默认变量值 */
.dashboard {
  container-name: dash;
  --theme: light;
  background: #fff;
}

/* 当变量值变成dark,内部文字和背景跟着翻 */
@container dash (--theme: dark) {
  .card {
    background: #222;
    color: #eee;
  }
  .card-title {
    border-bottom-color: #555;
  }
}

在JS里只需要改.dashboardstyle.setProperty('--theme', 'dark'),所有卡片样式自动切换。相比传统方法挨个加类名,这玩意儿省心多了。

实际操作时注意:自定义属性作为查询条件,不能写var(--theme),直接写(--theme: dark)就行。而且目前浏览器支持还不太统一,建议搭配@supports做降级。

@supports (container-type: style) {
  /* 支持样式查询的浏览器走新路子 */
  @container dash (--theme: dark) {
    .card { background: #222; }
  }
}
@supports not (container-type: style) {
  /* 不支持的浏览器用老办法 */
  .dashboard.dark .card { background: #222; }
}

方案B:干掉嵌套斜体的尴尬

写文章时经常碰到这种情况:一个blockquote里面已经用了斜体,结果引文里又来个emi标签,斜体叠斜体,看着贼难受。样式查询能根据外层容器的font-style自动把内层斜体掰正。

blockquote {
  container-name: quote;
  font-style: italic;
}

/* 如果blockquote本身已经是斜体,里面的斜体标签就强制变正常 */
@container quote (font-style: italic) {
  em, i, q, address {
    font-style: normal;
  }
}

这么一搞,不管blockquote从哪儿继承来的斜体,只要它算出来是斜的,里头的em就自动站直了。不用操心类名,不用写一堆嵌套选择器。

部署这套方案时,记得给blockquote加上container-name,不然查询找不到目标。另外font-style的继承性可能导致预料之外的结果,比如外层父级是斜体,但blockquote自己没设font-style,计算值也是斜体,照样触发查询。这反而是个优点,因为查询的就是计算后的真实值。

方案名称核心思路适用场景
自定义属性驱动改CSS变量值触发内部样式主题切换、状态面板
嵌套斜体修正检测父级斜体状态重置子元素富文本排版、引文区

最后提醒一嘴,目前样式查询在规范里还属于“毛坯房”阶段,语法可能微调。但基本原理已经定下来了:容器有啥样式,里面的孩子就跟着变。这货未来绝对是CSS界的王炸,现在提前上车,等浏览器全量支持时直接起飞。