@property - CSS: Cascading Style Sheets | MDN
At 规则 - CSS:层叠样式表 | MDN
Custom properties (–*): CSS variables - CSS: Cascading Style Sheets | MDN
CSS Houdini - Developer guides | MDN
@property
CSS at-rule 是 CSS Houdini API [🔗] 的一部分,它允许开发者显式地定义他们的CSS 自定义属性, 允许进行属性类型检查、设定默认值以及定义该自定义属性是否可以被继承**。
在过去,我们使用CSS自定义变量(CSS Variables)来存储和复用值,但它们并不具备类型检查和默认值设定的功能。
而CSS @property则弥补了这一空白,使得自定义属性更加功能丰富和强大。
@property --property-name {
syntax: "<color>";
inherits: false;
initial-value: #c0ffee;
}
--property-name
: 自定义属性名称syntax
: 定义了自定义属性接受的值的类型。
+
(空格分隔)和 #
字号(逗号分隔)的乘法器表示期望的是一个值的列表,
<color>#
意味着期望的语法是一个以逗号分隔的 <color>值列表
。<length> | auto
接受 <length>或auto
,而 <color># | <integer>#
期望的是以逗号分隔的 <color>值列表
或以 逗号分隔的<integer>值列表
。inherits
: 指定该自定义属性是否可以被子元素继承,默认为 false。initial-value
:设置自定义属性的默认值。@property 规则中 syntax
和 inherits
描述符是必需的;
如果其中任何一项缺失,整条规则都将失效并且会被忽略。
initial-value
描述符仅在 syntax 描述符为通用 syntax 定义时是可选的,否则initial-value也是必需的——如果此时该描述符缺失,整条规则都将失效且被忽略。
未知的描述符自身都是无效的,且会被忽略。但是不会造成整条@property规则的失效。
<div class="container">
<div class="item one">Item one</div>
<div class="item two">Item two</div>
<div class="item three">Item three</div>
</div>
--item-size
和 --item-color
,用它们来定义三个子元素 item
的宽度和高度以及背景颜色。/* --item-size and --item-color */
@property --item-size {
syntax: "<percentage>";
inherits: true;
initial-value: 40%;
}
@property --item-color {
syntax: "<color>";
inherits: false;
initial-value: aqua;
}
--item-size
:
<percentage>
;--item-color
:
<color>
类型aqua
.container {
display: flex;
height: 200px;
border: 1px dashed black;
/* 使用自定义属性 */
/* 在父元素 设置了自定义属性的值 */
--item-size: 20%;
--item-color: orange;
}
/* 使用自定义属性 设置 item的 宽高 和背景颜色 */
.item {
width: var(--item-size);
height: var(--item-size);
background-color: var(--item-color);
}
/* 设置自定义属性在元素自己身上的值 */
.two {
--item-size: initial;
--item-color: inherit;
}
.three {
/* 无效值 */
--item-size: 1000px;
--item-color: xyz;
}
🍀 分析:
两个自定义属性 --item-size: 20%
和 --item-color: orange;
设置在父级容器 container
上,覆盖了定义时设置的默认值: --item-size:40%
和 --item-color:aqua
。其中--item-size
为可继承; --item-color
不可继承。
对 class 为item
的子元素,通过自定义属性设置了 宽高和背景颜色。
20%
。因为,父容器重新设置 --item-size
的值。对于 one
,没有设置这些自定义属性。
--item-size
是可继承的,因此使用其父容器上设置的值20% 。--item-color
是不可继承的,因此不考虑父级上的 orange
。而是使用默认的初始值 aqua。对于 two
,对两个自定义属性 --item-size
、--item-color
设置了 CSS全局关键字,这两个属性对于所有值类型都是有效值,因此无论语法描述符的值如何都是有效的。
--item-size:initial
: 使用该属性的初始值。在 @property 声明中设置的初始值 initial-value
为 40%
;--item-color:inherit
: 表示从其父元素(也就是container
)继承 orange
。即使自定义属性被设置为不被继承,也要显式地从其父级继承orange
。对于 three
, --item-size
和 --item-color
都是无效值。
--item-size
值为 1000px
。虽然 1000px
是一个 <length>
值,但是@property 声明时要求该值是一个<percentage>
类型。因此该声明无效并被忽略,这意味着使用了父级上可继承的 20%
。--item-color
值为 xyz
也是无效的。
xyz
不是 CSS 数据类型 [<color>🔗](https://developer.mozilla.org/zh-CN/docs/Web/CSS/color_value)
的关键字 <color-name>
的有效值。所以会被忽略,所以直接显示的是 item
定义的样式。--item-color
不能被继承,因此使用 aqua
的默认值,也不使用父级的值 orange
。@property --colorA {
syntax: "<color>";
inherits: false;
initial-value: red;
}
@property --colorB {
syntax: "<color>";
inherits: false;
initial-value: yellow;
}
@property --colorC {
syntax: "<color>";
inherits: false;
initial-value: blue;
}
.box {
width: 300px;
height: 300px;
background: linear-gradient(45deg,
var(--colorA),
var(--colorB),
var(--colorC));
animation: animate 3s linear infinite alternate;
}
@keyframes animate {
20% {
--colorA: blue;
--colorB: #F57F17;
--colorC: red;
}
40% {
--colorA: #FF1744;
--colorB: #5E35B1;
--colorC: yellow;
}
60% {
--colorA: #E53935;
--colorB: #1E88E5;
--colorC: #4CAF50;
}
80% {
--colorA: #76FF03;
--colorB: teal;
--colorC: indigo;
}
}
<div class="box"></div>
<div class="section">
<div class="box bg mask1"></div>
<div class="box bg mask2"></div>
</div>
$img1: 'https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/191/191-bigskin-6.jpg';
$img2: 'https://game.gtimg.cn/images/yxzj/img201606/skin/hero-info/191/191-bigskin-8.jpg';
$mask1: linear-gradient(45deg, #000 0, #000 var(--per), transparent calc(var(--per) + 10%), transparent);
$mask2: conic-gradient(#000 0, #000 var(--per), transparent calc(var(--per) + 10%), transparent);
@property --per {
syntax: '<percentage>';
inherits: false;
initial-value: -10%;
}
.section {
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
.section div {
margin: 20px;
}
.section {
.box {
width: 600px;
height: 300px;
}
.bg {
background: url($img1);
background-repeat: no-repeat;
background-position: 50%;
background-size: cover;
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: url($img2);
background-size: cover;
background-position: 50%;
animation: animate 2s ease-in-out infinite alternate;
}
}
.mask1 {
&::after {
mask: $mask1;
}
}
.mask2 {
&::after {
mask: $mask2;
}
}
}
@keyframes animate {
0% {
--per: -10%;
}
100% {
--per: 100%;
}
}