你以为 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 支持了再用。从一个组件开始:
- 找一个用 JS 管理变体的组件(按钮、徽章、卡片)
- 把
classList.toggle('variant')换成el.style.setProperty('--variant', 'value') - CSS 里用
if(style(--variant: ...))响应属性值 - 写降级值在上面,支持 if() 的浏览器会覆盖
生产路径清晰,不需要等全面 Baseline。这是一件现在就能用、用了就有收益的事。
评论区
登录后可评论。