配了三年 CSS,每次换品牌色都要重写派生色值——今天 @property 把这件事彻底原生化了
做过设计系统的工程师都踩过这个坑:设计师改了品牌主色,工程师要手动把所有派生色重写一遍——亮色、深色、色相旋转、透明度调节,全是 hsl() 或 oklch() 嵌套的硬编码,一改少则几行,多则几十行,还容易漏。
问题出在哪?
传统方案要么靠 Sass build time 的 color.scale()、darken()、lighten(),要么靠 JS 在运行时动态算。两种都有代价:Sass 算出来的值烘烤进了 CSS,运行时换主题要靠 class 切换覆盖;JS 方案要引入颜色库或者手写 RAF 循环。
更关键的是——CSS 自定义属性(–brand-color)本质上是个字符串,浏览器不知道它是颜色、角度还是百分比,所以 transition 和 animation 在它上面直接失效:要么不动画,要么瞬间跳变。
@property 原生化了这件事
@property 是 CSS Houdini 的 CSS Properties and Values API,让你在 CSS 里直接给自定义属性上户口:
@property --brand-hue {
syntax: "<angle>";
inherits: false;
initial-value: 220deg;
}
三个字段:类型、是否继承、初始值。浏览器知道 –brand-hue 是个角度,就可以插值了:
@property --brand-hue {
syntax: "<angle>";
inherits: false;
initial-value: 220deg;
}
.wheel {
background: hsl(var(--brand-hue), 80%, 50%);
transition: --brand-hue 0.4s ease;
}
.wheel:hover {
--brand-hue: 360deg; /* 平滑旋转 */
}
不用 JS,不用库,就这一个声明,浏览器自己算中间帧,GPU 合成器线程驱动,零主线程开销。
渐变色动画:以前只能靠 JS,现在纯 CSS
以前想让渐变色平滑过渡,要用 requestAnimationFrame 循环更新样式。现在用 @property 声明渐变色两个端点,然后 transition 它们:
@property --stop-one {
syntax: "<color>";
inherits: false;
initial-value: #6366f1;
}
@property --stop-two {
syntax: "<color>";
inherits: false;
initial-value: #ec4899;
}
.card {
background: linear-gradient(135deg, var(--stop-one), var(--stop-two));
transition: --stop-one 0.4s ease, --stop-two 0.4s ease;
}
.card:hover {
--stop-one: #0ea5e9;
--stop-two: #22d3ee; /* 渐变色平滑过渡,零 JS */
}
这个模式可以用在进度环、颜色选择器、品牌换肤等场景。浏览器知道两个端点是什么颜色,直接在 OKLCH 色彩空间里插值——感知均匀,没有灰边。
和 Sass 比:谁取代谁?
| 维度 | Sass 变量 | @property |
|---|---|---|
| 类型安全 | 编译期检查,无运行时 | 注册时类型,浏览器插值 |
| 动画支持 | 不能 | 原生 transition/animation |
| 运行时动态切换 | 不能(烘烤进 CSS) | 可以(JS 改值,CSS 响应) |
| 颜色数学(color.scale() 等) | 支持 | 不支持 |
| 浏览器覆盖 | 所有浏览器(编译后是普通 CSS) | Chrome 85+/Firefox 128+/Safari 15.4+,~95% |
两者不互斥,是互补的:Sass 适合 build time 的派生逻辑(循环生成色阶、函数计算),@property 适合运行时的平滑过渡和动态主题。
浏览器支持:这不是实验性功能
@property 在 2024 年 7 月 Firefox 128 加入后正式进入 Baseline Widely Available:Chrome 85+、Firefox 128+、Safari 15.4+、Edge 85+,全球约 95% 覆盖。不支持的浏览器会忽略 @property 声明,自定义属性本身作为普通变量继续工作,属于天然渐进增强。
下一步
如果你的设计系统还在靠 Sass 的 color.scale() 或者手写派生色,先把主色的派生改成 @property 声明试试——一个 –brand-hue: 220deg 加几个 transition,就能让整个品牌色盘随主题动态切换,还不需要任何构建步骤。下一个坑——透明度调整——可以用 alpha() 函数配合 @property –alpha: 0.5 直接写,也是浏览器原生支持。
评论区
登录后可评论。