likes
comments
collection
share

使用 @property 为 CSS 属性指定数据类型在 2024 年 7 月发布的 Firefox 128 中,新增了

作者站长头像
站长
· 阅读数 52

在 2024 年 7 月发布的 Firefox 128 中,新增了 @property 规则,这项功能允许开发者定义 CSS 自定义属性(即变量)的类型、继承规则和初始值,从而使 CSS 更加安全和灵活。以下是对这项功能及其优势的简述:

传统后备值的局限性

在传统 CSS 中,如果使用自定义属性(变量),我们可能需要定义一个后备值,以确保在属性未被设置时仍有一个有效的值。然而,这种后备值机制有其局限性:

  • 类型安全性差:后备值不能指定数据类型,因此当自定义属性用于不同类型的值时,可能会出现不一致的表现。
  • 继承问题:默认的后备值机制无法控制自定义属性的继承行为,这可能导致样式的意外变化。
  • 维护复杂性:使用后备值时,可能需要手动处理各种情况和回退机制,这增加了维护的复杂性。

定义更安全的 CSS 类型

@property 规则通过允许开发者定义属性的类型、继承性和初始值,解决上述问题。其主要优势包括:

  • 类型定义:我们可以指定自定义属性的类型(如长度、颜色、百分比等),确保属性值符合预期,减少类型错误。
  • 初始值:可以为自定义属性定义明确的初始值,避免属性值缺失时产生的样式问题。
  • 继承控制:允许指定自定义属性的继承行为,使得在子元素中可以更灵活地控制样式的继承。
@property --main-color {
  syntax: '<color>';
  inherits: true;
  initial-value: #000;
}

.main {
  --main-color: #f00;
  color: var(--main-color);
}

通过以下方式提供类型syntax

为什么我们需要自定义属性的类型?有以下几个原因:

  • 类型有助于验证什么是有效值,什么是无效值
  • 如果没有类型,自定义属性就非常开放,几乎可以采用任何值,包括空格
  • 缺少类型会阻止浏览器 DevTools 提供有关自定义属性所用值的最佳详细信息

在我们的@property定义中,syntax描述符可以为自定义属性提供允许的类型。我们使用了"<color>",但其他类型包括:

  • "<length>"- 带有单位的数字,例如4px3vw
  • "<integer>"- 十进制单位 0 到 9(又称“整数”)
  • "<number>"- 可能有分数的数字,例如1.25
  • "<percentage>"- 带有百分号的数字,例如24%
  • "<length-percentage>"- 接受有效的<length><percentage>

一个特殊情况是"*"代表“通用语法”并允许接受任何值,类似于默认行为。这意味着可以跳过输入的好处,但可能需要继承和/或初始值控制。 该类型适用于自定义属性的计算值,因此该类型会对"<color>"两者感到满意(尽管我们很快就会了解到,只有其中之一被接受)。blue``light-dark(blue, cyan)``initial-value

使用列表进行更强的类型控制

假设我们想为我们的--color-blue自定义属性提供一些灵活性。

我们可以使用列表来提供有效选项。除这些确切值之外的任何值都将被视为无效,并使用initial-value(如果不适用继承)。这些被称为“自定义标识”,区分大小写,可以是任何值。

@property --color-blue {
  syntax: "blue | cyan | dodgerblue";
  inherits: true;
  initial-value: blue;
}

.color-blue {
  color: var(--color-blue);
}

.demo p {
  --color-blue: dodgerblue;
}

混合值类型

上一列表中使用的竖线字符 ( |) 表示“或”条件。虽然我们使用了显式的颜色名称,但它也可用于表示“这些语法类型中的任何一种都是有效的”。

syntax: "<color> | <length>";

输入多个值

到目前为止,我们只输入了只需要单个值的自定义属性。

另外两种情况可以用一个附加的“乘数”字符来覆盖,该字符应紧跟在语法组件名称之后。

  • 用于+支持空格分隔的列表,例如"<length>+"
  • 用于#支持逗号分隔的列表,例如"<length>#"

这对于允许多个定义的属性很有用,例如background-image

@property --bg-gradient{
  syntax: "<image>#";
  inherits: false;
  initial-value: 
    repeating-linear-gradient(to right, blue 10px 12px, transparent 12px 22px), 
    repeating-linear-gradient(to bottom, blue 10px 12px, transparent 12px 22px);
}

.box {
  background-image: var(--bg-gradient);

  inline-size: 5rem;
  aspect-ratio: 1;
  border-radius: 4px;
  border: 1px solid;
}

多部分混合值的输入

一些属性接受混合类型来开发完整的值,例如box-shadow具有潜在类型inset、一系列<length>值和的<color>

目前,无法在单个@property定义中输入此内容,尽管可以尝试类似的操作"<length>+ <color>"。然而,这实际上会使@property定义本身无效。

一个替代方案是拆分自定义属性定义,这样我们就可以允许一系列长度,然后允许一种颜色。虽然稍微麻烦一点,但这仍然让我们能够享受打字的好处,从而减少我们之前提到的潜在错误。

@property --box-shadow-length {
  syntax: "<length>+";
  inherits: false;
  initial-value: 0px 0px 8px 2px;
}

@property --box-shadow-color {
  syntax: "<color>";
  inherits: false;
  initial-value: hsl(0 0% 75%);
}

.box {
  box-shadow: var(--box-shadow-length) var(--box-shadow-color);

  inline-size: 5rem;
  aspect-ratio: 1;
  border-radius: 4px;
}

允许任何类型

如果不太关心属性的“类型”,box-shadow而更关心继承或初始值,则可以使用通用语法定义来允许任何值。initial-value仍然需要计算独立

@property --box-shadow {
  syntax: "*";
  inherits: false;
  initial-value: 0px 0px 8px 2px hsl(0 0% 75%);
}

有效使用initial-value

除非语法使用通用语法定义对任何值开放 - "*"- 那么就需要设置一个initial-value才能获得注册自定义属性的好处。

正如我们已经体验过的,使用 是防止由于计算值时间无效initial-value而导致渲染完全中断的情况的关键。以下是使用的其他一些好处。**@property``initial-value

在构建设计系统或 UI 库时,确保自定义属性强大且可靠非常重要。提供initial-value自定义属性有助于防止体验不佳。此外,键入属性也与保持设计令牌的意图完美契合,设计令牌可以表达为自定义属性。

动态计算场景(例如使用)clamp()可能会包含无效值,无论是由于错误还是由于浏览器不支持函数中的某些内容。使用后备方式initial-value可确保设计保持功能。此后备行为也是对不受支持功能的保障,尽管这可能会受到@property所使用的浏览器是否支持该规则的限制。

限制initial-value

取决于initial-valuesyntax为 定义的@property。除此之外,syntax本身并不支持我们之前介绍过的所有可能的值组合。因此,有时需要一点创造力才能获得好处。

此外,initial-value值必须是规范所称的计算无关的。简而言之,这意味着相对值(如em或动态函数(如clamp()或 ))不幸是不允许的。但是,在这些情况下,您仍然可以设置可接受的初始值,然后在使用自定义属性时(例如在赋值中light-dark())使用相对值或动态值。**:root

@property --heading-font-size {
  syntax: "<length>";
  inherits: true;
  initial-value: 24px;
}

:root {
  --heading-font-size: clamp(1.25rem, 5cqi, 2rem);
}

这种对相对单位或动态函数的限制还意味着其他自定义属性不能在分配中使用 initial-value 。以前的技术仍然可以用于缓解这种情况,其中首选结果由属性的使用组成。

最后,通过 注册的自定义属性@property仍然受限于常规属性的规则,例如它们不能用于启用媒体或容器查询规则中的变量。例如,@media (min-width: var(--mq-md))仍然无效。

转载自:https://juejin.cn/post/7407109654105473034
评论
请登录