配了三年渐变动画,每次从蓝变紫都要靠 JS 插值——今天 CSS 自己会了,@property 把这件事彻底原生化了
配了三年渐变动画,每次从蓝变紫都要靠 JS 插值或者直接跳变——今天 CSS 自己会了,@property 把这件事彻底原生化了。
背景:为什么 CSS 变量不能做动画?
传统 CSS 变量的本质是无类型的字符串:
.card {
--gradient: linear-gradient(135deg, #667eea, #764ba2);
transition: --gradient 0.5s;
}
浏览器拿到 --gradient 只知道它是个字符串,不知道它是渐变色、角度还是数字——所以 transition 直接忽略它。这是 CSS 变量从第一天就有的局限,很多团队因此上了 GSAP 或者写 JS 定时器。
@property 三件套:让变量「有型」
CSS Houdini 里的 @property 允许你注册一个带类型约束和初始值的变量,浏览器从此「认识」这个变量的语义:
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
@property --grad-color-1 {
syntax: "<color>";
inherits: false;
initial-value: #667eea;
}
@property --grad-color-2 {
syntax: "<color>";
inherits: false;
initial-value: #764ba2;
}
三个字段:
syntax:类型声明,支持<color>、<angle>、<length>、<integer>、<gradient>等 20+ 类型inherits:false= 不向子元素扩散,组件自有;true= 向下继承initial-value:初始值,必须符合 syntax 声明的类型
注册完之后,浏览器终于能理解这些变量,从而让 transition 和 animation 起效。
场景一:渐变色 hover 平滑过渡
hover 时渐变色丝滑切换:
.card {
--grad-color-1: #667eea;
--grad-color-2: #764ba2;
background: linear-gradient(135deg, var(--grad-color-1), var(--grad-color-2));
transition: --grad-color-1 0.4s, --grad-color-2 0.4s;
}
.card:hover {
--grad-color-1: #f093fb;
--grad-color-2: #f5576c;
}
hover 时两个颜色值平滑插值过渡,完全原生 CSS,没有任何 JS。
场景二:旋转渐变边框(持续动画)
用 @property 驱动 @keyframes,让动画一直跑:
@property --angle {
syntax: "<angle>";
inherits: false;
initial-value: 0deg;
}
.gradient-border {
--angle: 0deg;
border: 4px solid;
border-image: conic-gradient(
from var(--angle),
#ff6b6b, #4ecdc4, #45b7d1, #ff6b6b
) 1;
animation: spin-angle 3s linear infinite;
}
@keyframes spin-angle {
to { --angle: 360deg; }
}
conic-gradient 的起始角度是变量驱动,animation 让它持续旋转——这在以前只有伪元素 + canvas 或者 GSAP 才能做,现在纯 CSS 几行搞定。
场景三:骨架屏脉冲(number 类型)
<number> 类型也支持,这在骨架屏 loading 动画里很实用:
@property --pulse-lightness {
syntax: "<percentage>";
inherits: false;
initial-value: 90%;
}
.skeleton {
background: linear-gradient(
90deg,
hsl(210, 10%, 90%),
hsl(210, 10%, var(--pulse-lightness)),
hsl(210, 10%, 90%)
);
background-size: 200% 100%;
animation: pulse-bg 1.5s ease-in-out infinite;
}
@keyframes pulse-bg {
0%, 100% { --pulse-lightness: 90%; }
50% { --pulse-lightness: 96%; }
}
比传统的 background-position 滑动方案更直观,动画控制更精确,而且一个变量管一个效果,改起来容易。
场景四:组件级样式隔离(inherits: false)
@property 的 inherits: false 还有一个和动画无关但同样重要的用法——组件样式隔离:
@property --card-bg {
syntax: "<color>";
inherits: false;
initial-value: white;
}
.card {
--card-bg: #f0f0f0;
background: var(--card-bg);
}
.card.warning {
--card-bg: #fff3cd;
}
.card .child {
background: var(--card-bg);
}
父元素覆盖 --card-bg,子元素如果也声明了同名变量(且 inherits: false),会保持自己的初始值。这和 CSS 变量默认继承行为正好相反,是做设计系统 token 隔离的好工具。
兼容性:可以上生产了
- Chrome/Edge 85+
- Firefox 128+
- Safari 17.2+
主流浏览器全部就位,生产环境完全可以直接用。搭配 @supports 做渐进增强:
@supports (background: linear-gradient(red, blue)) {
.card {
/* 浏览器支持渐变过渡增强 */
}
}
下一步
下次遇到「渐变色不能动画」「hover 颜色跳变」,先检查有没有注册 @property——这比找 GSAP 文档快多了。如果你有多个组件都要独立动画,把 inherits: false 配上,设计系统的 token 隔离也顺带解决了。
评论区
登录后可评论。