@property 必须在 :root、顶层 @layer 或独立 CSS 文件顶层声明,且 syntax 与 initial-value 类型严格匹配,注册后才支持 transition 和 @keyframes 插值动画。

必须写在 :root 或 @layer 顶层,嵌套就失效
@property 不是选择器内能随便塞的声明。把它放进 .card、@media 甚至 @layer components 里,浏览器直接跳过——不报错,也不注册,后续所有 transition: --size 都静默失败。
真正有效的写法只有这几种:
:root { @property --size { syntax: "<length>"; inherits: false; initial-value: 16px; } }</length>-
@layer base { @property --size { ... } }(且该@layer不能被任何选择器包裹) - 独立 CSS 文件顶层(
@import进来时,也必须确保它没被包在任何块里)
本质不是“位置限制”,而是 @property 在向 CSS 引擎注册一个全局类型契约,不是局部变量声明。
syntax 和 initial-value 必须严格类型匹配
写 syntax: "<color>"</color> 却配 initial-value: 0,整条规则被丢弃;写 syntax: "<number>"</number> 却配 initial-value: "10"(带引号),同样失效。浏览器连 initial-value 都不读,直接忽略整条 @property。
立即学习“前端免费学习笔记(深入)”;
常见匹配组合:
-
syntax: "<color>"</color>→initial-value: #3498db、rgb(52 152 219)、hsl(204 71% 53%)(但不能是red字符串?不,red合法,它是标准颜色关键字) -
syntax: "<length>"</length>→initial-value: 1rem、24px(缺单位如24就不行) -
syntax: "<number>"</number>→initial-value: 1.5(不能加引号,不能写成"1.5") -
syntax: "<angle>"</angle>→initial-value: 45deg(45不行,45rad行)
syntax: "*" 虽然兜底,但会放弃插值能力——比如 --x: 0deg → --x: 360deg 动画可能卡顿或跳变。
只有注册后,var(--x) 才真能参与 transition 和 @keyframes
没注册的 --x 是纯字符串。你写 transition: --x 0.3s,浏览器不报错,但不会动——它不知道怎么在 red 和 blue 之间算中间帧。
注册后,浏览器才启用对应类型的插值算法(如 sRGB 插值、角度线性插值)。但要注意现实约束:
-
transform: rotate(var(--deg))在 Chromium 中常不触发动画,得写成rotate(calc(var(--deg) * 1))强制数值上下文 - 渐变动画不能只靠一个
--color,得拆成--start-color和--end-color分别注册 - @keyframes 里必须用
from { --x: red; }/to { --x: blue; },不能写--x: red; --x: blue;在同一块里
浏览器支持仍是硬门槛,别默认可用
截至 2026 年 9 月,@property 仅在 Chrome 110+、Edge 110+、Opera 96+ 原生支持;Firefox 仍需手动开启 layout.css.property-rule.enabled 实验标志;Safari 完全不支持。
这意味着:
- 不能把它当基础样式能力用,必须有降级方案(比如 fallback 到 JS 控制或 class 切换)
- 在构建流程中,若用 PostCSS 或 UnoCSS 等工具生成
@property,得确认输出目标不包含 Safari 用户 - CI/CD 中的视觉回归测试容易漏掉这个特性失效场景——因为控制台无报错,只表现为空白动画
最易被忽略的一点:哪怕你写了完全合规的 @property,只要加载顺序不对(比如注册语句在使用它的 CSS 之后才解析),动画依然不动。顺序和作用域,比语法本身更常出问题。


















