配了三年 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 直接写,也是浏览器原生支持。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 14 阅读