扩展CSS的when/else链。初探

199 阅读6分钟

CSS作者使用CSS条件规则来定义一组基于处理器或样式表所应用的文档的能力的规则。其中一些规则使作者能够在样式表中执行逻辑。

例如,CSS作者使用@media 来进行媒体查询,并有选择地将样式表应用于一个文档。同样,@support 规则被用来查询用户的代理对新的CSS功能的支持。

就其本身而言,这些规则是非常有用的。然而,很多时候,CSS作者可能需要结合这些规则来创建条件语句。这通常是由于不同的样式规则在不同的情况下适用。

目前,这种行为在CSS中的实现是相当棘手的,因为作者必须小心翼翼地制作样式表,以考虑到所有的条件。

关于这些问题,有两个新的条件规则被提议来解决:@when@else 。在发布的时候,该 [@when](https://tabatkins.github.io/specs/css-when-else/) 建议CSSWG决议采纳为 "下一层次的CSS条件"

但它们是什么,我们怎样才能使用它们呢?

在这篇文章中,我们将初步了解这些新的条件规则,包括它们在样式表中的一些实际用途。

创建泛化的条件规则@when

提议的@when 规则概括了条件规则,所以你可以概括你的规则块来使用两种或更多种查询,而不是为一个特定的任务使用特定的条件规则,比如用@support 的特征查询,。

你可以把它看作是其他CSS条件规则的包装器,如@media@support

因此,假设你想测试媒体屏幕是否低于769像素,并执行两种特征查询,以检查浏览器是否同时支持网格和柔性显示功能。如果true ,你还想在每个网格列中只设置一个项目。

如果没有@when ,你可能要写与此类似的东西。

@media (max-width: 769px) {
    @supports (display: grid) and (display: flex) {
        .grid {
            grid-template-columns: 1fr;
        }

        .flex {
            flex-direction: row;
        }
    }
}

在上面的代码中,我们在激活下面的样式表之前检查三个条件,并使用两个不同的条件规则。

但有了@when ,我们可以创建一个通用的规则,将所有不同的条件都包在一个语句中。

@when media(max-width: 769px) and supports(display: grid) and supports(display: flex) {
    .grid {
        grid-template-columns: 1fr;
    }

    .flex {
        flex-direction: row;
    }
}

这很好,但它提出了以下问题:如果@when 语句中的条件(查询)评估为false ,会发生什么?

嗯,从技术上讲,什么也不会发生。如果@when 中的条件失败了,CSS引擎就会忽略它的样式块并返回到默认样式。

同样,这对于简单的情况是很好的。但是,如果你想创建比两个条件更长的东西呢?例如,如果条件一是true ,我们想应用样式块一。否则,如果条件二是true ,我们想应用样式二。如果两个条件都不是true ,我们就退回到默认样式。

CSS条件的第4级规范提出了在CSS中创建条件规则链的@else 规则。

在内联中使用@when

@when 也可以内联使用。在下面的例子中,我们将在一个元素上设置一个每列三个项目的网格。然后,当width 小于400像素时,我们必须改变为每列网格有一个项目。

.container {
    width: 80%;
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    padding: 1rem;
    @when element(max-width: 400px) {
        grid-template-columns: 1fr;
        padding: 2rem;
    }
}

通过在上面的样式块内使用@when ,我们可以指定不同的样式规则,在元素达到规定的条件时激活,比如当它的宽度小于400px时。

创建条件链@else

@else 条件规则允许作者创建一系列的条件规则,从而形成一个条件链。

在前面的代码中,我们创建了一个通用的条件,如果条件是true ,就可以制作媒体,支持查询,并应用一组样式。然而,在@when 中指定的条件评估为假的情况下,没有回退条件。

通过使用@else ,我们可以创建一个条件语句链,每个条件都包含一个要评估的条件。

如果所有其他条件评估为false ,在链的末端有一个回退条件。

@when media(min-width: 768px) and supports(display: grid) {
    div {
        display: grid;
        grid-template-columns: 1fr;
    }
  } @else supports(clip-path: circle(1px)) and supports(transform: skewY(1deg)) {
        div {
            display: block;
         }

         .blog img {
          clip-path: circle(50%);
          }

          .showcase {
          transform: skewY(10deg);
          }
    }
  } @else {
    /* Fallback. In case all of the above conditions evaluates to false */
      div {
        display: block;
      }

      img {
        width: 100%;
        height: 100%;
      }
}

如果没有条件链,这就是你实现同样事情的方法。

  /* If  width is smaller than 769px */
@media (max-width: 768px) {
    @supports (display: grid) {
      div {
        grid-template-columns: 1fr;
      }
    }
    @supports not (display: grid) {
      @supports (clip-path: circle(1px)) and (transform: skewY(1deg)) {
        div {
            display: block;
        }

        .blog img {
            clip-path: circle(50%);
        }

        .showcase {
            transform: skewY(10deg);
          }
      }
      @supports not ((clip-path: circle(1px)) and (transform: skewY(1deg))) {
        div {
            display: block;
        }

        .blog img {
            width: 100%;
            height: 100%;
        }
      }
    }
  }

  /* If  width is larger than 769px */
  @media (min-width: 769px) {
    @supports (clip-path: circle(1px)) and (transform: skewY(1deg)) {
           div {
            grid-template-columns: repeat(3, 1fr);
            }

            .blog img {
              clip-path: circle(50%);
            }

            .showcase {
              transform: skewY(10deg);
            }
    }
    @supports not ((clip-path: circle(1px)) and (transform: skewY(1deg))) {
         div {
            grid-template-columns: repeat(3, 1fr);
          }

          .blog img {
            width: 100%;
            height: 100%;
          }
      }
  }

与条件链相比,这段代码是不可读的,而且很多内容在各个代码块中都是重复的。你的造型越复杂,一切都变得越困难。

注意,在条件链中,只有一条规则被选中。例如,如果第一条规则的条件评估为true ,那么链中的后续规则必须评估为false

编写媒体查询的新方法

到目前为止,我们使用min-widthmax-width 来设置造型的断点。从语义上讲,这些术语可能很难理解。

min-width 指的是大屏幕,而max-width 指的是小屏幕。这可能会变得相当棘手,特别是对于初学者来说。

考虑到这一点,在媒体查询的第4级规范中引入了一种更有语义的方式来编写查询条件。

所以,与其这样写你的媒体查询,不如这样写。

@media (max-width: 769px) {
    .grid {
        grid-template-columns: 1fr;
    }

    .flex {
        flex-direction: row;
    }
}

@media (min-width: 769px) {
    .grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .flex {
        flex-direction: column;
    }

    body {
        text-align: center;
    }
}

你可以很快用这种方式来写

@media (width >= 769px) {
    .grid {
        grid-template-columns: repeat(3, 1fr);
    }

    .flex {
        flex-direction: column;
    }
}

@media (width <= 769px) {
    .grid {
        grid-template-columns: 1fr;
    }

    .flex {
        flex-direction: row;
    }
 }
}

与使用minmax 属性相比,这种语法更有意义,因为你不需要了解max-widthmin-width 到底代表什么。使用比较运算符使媒体查询更容易阅读和理解。

支持对CSS条件规则的扩展

除了@when@else ,第4级更新还包括支持规则的扩展,允许对支持的字体技术进行测试。

CSSWG条件规则模块第4级草案提供了一个很好的例子,说明新的条件规则如何查询支持的字体技术。

@when font-technology(color-COLRv1) and font-technology(variations) {
    @font-face { font-family: icons; src: url(icons-gradient-var.woff2); }
}
@else font-technology(color-SVG) {
    @font-face { font-family: icons; src: url(icons-gradient.woff2); }
}
@else font-technology(color-COLRv0) {
    @font-face { font-family: icons; src: url(icons-flat.woff2); }
}
@else {
    @font-face { font-family: icons; src: url(icons-fallback.woff2); }
}

因此,在上面的代码中,我们正在测试三种不同的字体颜色技术。第一条规则是测试COLRv1 ,它同时支持渐变和字体变化。如果浏览器支持它们,它就会相应地设置字体,而忽略其余的链。

否则,它就测试第二条规则,以此类推。最后,只有一种字体样式会被激活。

结论

@when@else 可能会被采纳,但并非没有问题。有一场强烈的辩论反对使用 [@when](https://github.com/w3ctag/design-principles/issues/335)因为@if 被认为是一个更常见的名字。

但是,@when ,以避免与流行的CSS预处理程序Sass发生冲突。

SASS已经使用了一个叫做@if 的规则,它控制一个样式块是否被评估或激活。

然而,其他人则建议使用不同的名字,如@where

不管它的名字如何,不能否认的是,这些规则在被网络浏览器正式实施后将非常有用。

The postExtending CSS when/else链。第一次出现在LogRocket博客上。