“如果” CSS 获取内联条件

Avatar of Geoff Graham
Geoff Graham

DigitalOcean 提供适用于您旅程各个阶段的云产品。 立即开始使用 价值 200 美元的免费积分!

几周前,当 CSS 工作组 (CSSWG) 决定在 CSS 值模块级别 5 规范中添加一个 if() 条件 时,一些警报响了起来。 是 Lea Verou 在同一天发布的 X 帖子引起了我的注意。

Lea 是 GitHub 问题 的发起者,该问题引发了讨论,而且巧合的是,或者说是冥冥之中,决议是在她的生日当天提出的。 那天一定很热闹! “你生日收到了什么礼物?” “哦,你知道的,只是一个被 CSS 规范接受的提案。” 太疯狂了,太疯狂了。

该被接受的提案为 CSSWG 开启了绿灯,使其可以着手研究这一想法,并有意传播一份规范草案,以便在最终成为推荐的 CSS 功能之前,进一步征求意见和考虑。 因此,这一切都需要很长时间才能实现,也就是说,如果它能够完全实现。

但根据条件要求应用样式的想法非常令人兴奋,值得早期研究。 我在 Lea 发布到 X 的同一天在我的博客上 记了一些关于它的笔记,并认为应该将它们整理到这里以备后用,同时总结自那时起出现的更多细节。

这不是一个新想法

许多提案都源于先前被拒绝的提案,if() 也不例外。 事实上,我们最近获得了几个允许进行条件样式设置的 CSS 功能——:has()容器样式查询 是两个最明显的例子。 Lea 甚至引用了 2018 年的票据,它看起来很像被接受的提案。

有什么区别?

样式查询已经发布,我们可以简单地引用相同的条件语法(加上来自 Tab 的 @when 提案media()supports()),而在 2018 年的提案中,条件的工作方式在很大程度上是未定义的。

Lea Verou,“CSS 中的内联条件?”

我喜欢 Lea 指出 CSS 始终是条件语言的方式

朋友们……从一开始,CSS 就有条件。 每个选择器本质上都是一个条件!

Lea Verou,“CSS 中的内联条件?”

没错! 瀑布模型是评估选择器并将它们与页面上的 HTML 元素进行匹配的工具。 if() 带来的作用是使用选择器编写内联条件。

语法

它归结为以下内容

<if()> = if( <container-query>, [<declaration-value>]{1, 2} )

…其中

  • 值可以嵌套以生成多个分支。
  • 如果没有提供第三个参数,它将等效于空令牌流。

目前这一切都是概念性的,没有什么是板上钉钉的。 当 CSSWG 处理该功能时,我们可能会看到一些变化。 但是,就目前而言,该想法似乎围绕着指定条件以及设置两种声明的样式之一——一种作为“默认”样式,一种作为匹配发生时的“更新”样式。

.element {
  background-color:
    /* If the style declares the following custom property: */
    if(style(--variant: success),
      var(--color-green-50), /* Matched condition */
      var(--color-blue-50);  /* Default style */
    );
}

在这种情况下,我们正在寻找一个 style() 条件,其中一个名为 --variant 的 CSS 变量被声明并设置为 success 值,并且

  • …如果 --variant 设置为 success,我们将 success 的值设置为 --color-green-50,它是一个映射到某个绿色色值的变量。
  • …如果 --variant 未设置为 success,我们将 success 的值设置为 --color-blue-50,它是一个映射到某个蓝色色值的变量。

默认样式是可选的,因此我认为在某些情况下可以省略它,从而提高可读性。

.element {
  background-color:
    /* If the style declares the following custom property: */
    if(style(--variant: success),
      var(--color-green-50) /* Matched condition */
    );
}

上面的语法定义提到,除了匹配条件和默认样式之外,我们还可以支持第三个参数,该参数允许我们在条件中嵌套条件。

background-color: if(
  style(--variant: success), var(--color-success-60), 
    if(style(--variant: warning), var(--color-warning-60), 
      if(style(--variant: danger), var(--color-danger-60), 
        if(style(--variant: primary), var(--color-primary)
      )
    ),
  )
);

哇,看起来那里发生了一些疯狂的“盗梦空间”! Lea 接着提出了一种语法,它将产生一个更加扁平的结构。

<if()> = if( 
  [ <container-query>, [<declaration-value>]{2}  ]#{0, },
  <container-query>, [<declaration-value>]{1, 2} 
)

换句话说,嵌套条件更加扁平,因为它们可以初始条件之外声明。 与之前相同的概念,但语法不同。

background-color: if(
  style(--variant: success), var(--color-success-60), 
  style(--variant: warning), var(--color-warning-60),
  style(--variant: danger), var(--color-danger-60), 
  style(--variant: primary), var(--color-primary)
);

因此,与其在一个 if() 语句中嵌套另一个 if() 语句,不如将所有可能的匹配条件都组合到一个语句中。

我们试图通过查询元素的样式来匹配 if() 条件。 没有相应的 size() 函数来查询尺寸——容器查询隐式地假设尺寸。

.element {
  background: var(--color-primary);

  /* Condition */
  @container parent (width >= 60ch) {
    /* Applied styles */
    background: var(--color-success-60);
  }
}

当我们调用 style() 函数时,容器查询就变成了样式查询。

.element {
  background: orangered;

  /* Condition */
  @container parent style(--variant: success) {
    /* Applied styles */
    background: dodgerblue;
  }
}

在我看来,在 if() 的上下文中看待样式查询更有意义。 没有 if()很容易质疑样式查询的一般用途。 但从这个角度来看,很明显样式查询是比容器查询本身大得多的图景的一部分。

关于 if() 语法,还有很多事情需要弄清楚。 例如,Tab Atkins 描述了一种可能导致混淆的场景,即匹配条件和默认样式参数是什么。 因此,谁知道最终会如何呢!

支持其他条件的条件

正如我们已经指出的,if() 远不是 CSS 中提供的唯一类型的条件检查。 编写检查其他条件(如 @supports@media)的内联条件语句会是什么样子?

在代码中

background-color: if(
  supports( /* etc. */ ),
  @media( /* etc. */ )
);

挑战在于容器支持尺寸查询。 正如前面提到的,没有明确的 size() 函数; 相反,它更像是一个匿名函数。

@andruud 在 GitHub 讨论中 简明扼要地描述了这一挑战

我不明白为什么我们不能使用 supports()media(),但尺寸查询会导致布局循环,这很难甚至不可能检测到。 (这就是为什么我们最初需要对尺寸 CQ 进行目前限制的原因。)

“我们不能已经使用 [X] 方法做到这一点吗?”

当我们之前查看语法时,您可能已经注意到,if() 与自定义属性一样重要,因为它与条件有关。 几年来,出现了许多变通方法来模拟我们通过 if() 获得的结果,我们可以根据自定义属性是否等于 01 来有条件地设置其值,包括

  • 使用自定义属性作为布尔值来应用样式或不应用样式,具体取决于它是否等于 01。(Ana 写了一篇关于此内容的精彩文章。
  • 使用一个带空值的占位符自定义属性,当另一个自定义属性被设置时,该占位符自定义属性会被设置,即 Chris 所描述的 “自定义属性切换技巧”
  • 容器样式查询! 问题(除了缺乏实现之外)是容器只对它们的子元素应用样式,即它们不能在满足某个条件时将其自身应用样式,而只能对其内容应用样式。

Lea 在一篇名为 “CSS 中的内联条件语句,现在?” 的单独文章中深入探讨了这一点,文章中包含一个表格,对各种方法进行了概述和比较,我将在下面直接粘贴该表格。 解释充满了复杂的 CSS 术语,但对于理解对 if() 的需求以及它与我们多年来使用的巧妙“技巧”相比如何非常有用。

方法输入值输出值优点缺点
二元线性插值数字定量可以用作值的一部分输出范围有限
切换var(--alias) (实际值太奇怪,无法直接公开)任何可以用作值的一部分需要别名的奇怪值
暂停动画数字任何正常,分离的声明接管 animation 属性

瀑布模型怪异
类型磨练关键字syntax 描述符支持的任何值暴露的 APIGood 封装具有高度灵活性必须将 CSS 插入到 light DOM 中

代码繁琐(虽然可以用构建工具自动化)

不支持 Firefox(不过正在改变
可变动画名称关键字任何正常,分离的声明在 Shadow DOM 之外不切实际,因为会发生名称冲突

接管 animation 属性

瀑布模型怪异

Lea,生日快乐!

虽然迟了两周,但感谢你与我们分享你生日大餐的喜悦!🎂

参考文献