你以为 CSS 变体必须靠 JS 切 class?Chrome 137 把这件事用 if() 彻底原生化了

每次改个按钮颜色都要在 JS 里写 .btn.primary.btn.danger.btn.outlined 四个类,然后在 React/Vue 组件里 classList.toggle('primary', isPrimary)CSS 本身对”变体”这件事从来没有发言权。

2025年5月,Chrome 137 把这件事彻底变了。

Chrome 137:CSS 终于能写 if 了

if() 函数允许你在属性值里直接写条件逻辑,三种查询类型:

/* style() — 查自定义属性,等同组件 props */
background: if(
  style(--variant: primary): oklch(60% 0.2 240);
  style(--variant: danger): oklch(55% 0.22 20);
  else: oklch(65% 0.05 240)
);

/* media() — 内联媒体查询,替代单独 @media 块 */
width: if(media(any-pointer: fine): 30px; else: 44px);

/* supports() — 特性检测,渐进增强 */
color: if(supports(color: oklch(0.7 0.15 240)): oklch(0.7 0.15 240); else: #3b82f6);

组件端只需要:

<button style="--variant: danger">删除</button>

不再需要 classList.toggle('danger'),不再需要 isDanger 状态变量,CSS 自己读 --variant 的值决定用什么颜色。变体 API 从 JS 迁回 CSS。

三个场景看 if() 改变了什么

1. 设计系统变体:原来 12 行,现在 3 行

旧方案:

.btn { background: gray; }
.btn.primary { background: blue; }
.btn.danger { background: red; }
el.classList.toggle('primary', isPrimary);
el.classList.toggle('danger', isDanger);

新方案:

.btn {
  background: if(
    style(--variant: primary): blue;
    style(--variant: danger): red;
    else: gray
  );
}
<button style="--variant: danger">删除</button>

JS 只负责写属性,不负责知道”danger 对应什么颜色”。变体定义和变体触发完全分离。

2. 响应式样式:条件和属性写在一起

旧方案: 同一个属性在两个 @media 块里重复:

button { width: 44px; }
@media (any-pointer: fine) {
  button { width: 30px; }
}

新方案:

button {
  width: if(media(any-pointer: fine): 30px; else: 44px);
}

媒体查询的条件和它影响的属性写在一起,阅读时不需要在两个块之间跳。

3. 设计 token 渐进增强

.element {
  /* 老浏览器:固定色值 */
  border-color: #c00;
  /* 新浏览器:按主题色派生 */
  border-color: if(
    supports(color: oklch(60% 0.2 20)): oklch(60% 0.2 20);
    supports(color: lch(60% 100 30)): lch(60% 100 30);
    else: #c00
  );
}

不需要 JS 做特性检测,不需要 CSS 变量hack,同一个属性声明里渐进增强。

浏览器现状

浏览器 支持情况
Chrome/Edge 137+(2025年5月)✅
Chrome Android 139+ ✅
Firefox 开发中 ❌
Safari 路线图上 ❌
Baseline 尚未进入(35%全球覆盖)

Chrome 137 发布已超过一年,但 Safari 和 Firefox 仍未支持。生产环境写法:

/* 先写降级值,所有浏览器认识 */
background: gray;
/* 渐进增强,Safari/Firefox 忽略这条 */
background: if(style(--variant: primary): blue; else: gray);

if() + style() 改变了什么架构

传统 CSS 变体靠”类名”作为 JS 和 CSS 的接口:

JS 知道:isPrimary → toggle('primary') → CSS 知道 .btn.primary

if() + style() 把这个接口变成了”属性”:

JS 知道:--variant="primary" → CSS 知道 style(--variant: primary)

区别在于:类名是视觉约定的字符串,属性是结构化数据。用属性驱动样式,组件的变体 API 从”这个元素有哪些 class”变成”这个元素有哪些状态”——更接近组件 props 的语义,TypeScript 的类型检查也可以直接对 --variant 的取值做约束。

配合 @layer 做优先级管理、@scope 做作用域隔离,if() 是 CSS 从”声明式规则列表”到”逻辑感知样式语言”转变的又一块拼图。

怎么用:选一个真实组件试

不要等 Safari 支持了再用。从一个组件开始:

  1. 找一个用 JS 管理变体的组件(按钮、徽章、卡片)
  2. classList.toggle('variant') 换成 el.style.setProperty('--variant', 'value')
  3. CSS 里用 if(style(--variant: ...)) 响应属性值
  4. 写降级值在上面,支持 if() 的浏览器会覆盖

生产路径清晰,不需要等全面 Baseline。这是一件现在就能用、用了就有收益的事。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 15 阅读