CSS 自定义属性 7 个有用的小知识

CSS 自定义属性(又称 CSS 变量 - CSS variables)允许我们在样式表中存储并复用属性变量。如果你还不熟悉它们,你可能在 CSS 预处理器变量中使用过它们。这些天我正在我的工作中大量使用自定义属性,所以我想在这里总结一些使用情况。

这篇文章不是关于 CSS 自定义属性如何工作的深度教程,如果需要了解一些基础,阅读下面推荐的这些资源:

#颜色函数

CSS 自定义属性不仅仅是属性值,它们也可以用于存储一组数据。一个常用的例子是 CSS 颜色函数值。HSLA 特别适合 CSS 自定义属性,允许开发者混合颜色时使用高级别的控制。

.some-element {
  background-color: hsla(
    var(--h, 120),
    var(--s, 50),
    var(--l, 50),
    var(--a, 1)
  );
}

.some-element.darker {
  --l: 20;
}

我们也可以做一些非常酷的事像计算辅助颜色集。这篇文章是一篇使用 CSS 自定义属性操作颜色的深度教程,Sara Soueidan 也同样有一篇很棒的同类文章。

#简写属性

如果你使用像 animation 这样的简写属性,并且需要更改不同元素的值,然后重写整个属性可能是错误方式并且额外的操作很繁重。使用 CSS 自定义属性可以很容易在简写属性中调整单个值:

.some-element {
  animation: var(--animationName, pulse) var(--duration, 2000ms) ease-in-out
    infinite;
}

.some-element.faster {
  --duration: 500ms;
}

.some-element.shaking {
  --animationName: shake;
}

#重复值

考虑我们有一个元素在它的内边距拥有相同的值,但是相同的值在它的外侧。像下面这样可能有一点单调乏味,特别是需要我们调整这些边距值的时候:

.some-element {
  padding: 150px 20px 20px 20px;
}

@media (min-width: 50em) {
  padding: 150px 60px 60px 60px;
}

使用 CSS 自定义属性意味着我们仅需要更改一次边距值。甚至,如果它是整个网站使用的标准值,我们可以在变量集中声明它,如配置文件或者网站的 design tokens

:root {
  --pad: 20px;
}

@media (min-width: 50em) {
  :root {
    --pad: 60px;
  }
}

.some-element {
  padding: 150px var(--pad) var(--pad) var(--pad);
}

#复杂计算

CSS 自定义属性可以存储计算值(用于 calc 函数),甚至可以用于计算其它自定义属性。一个例子是先前提到的计算辅助颜色。另一个是计算属性的相反值。我使用 CSS 自定义属性为 CSS Tricks 写过一篇文章 calculating the reverse of an easing curve

当需要计算路径 path 的相反量时,我经常与 clip-path 一起使用 CSS 自定义属性。下面的代码是使用两个伪元素的计算剪切路径分割一个元素。

<h1 style="--text: 'His Dark Materials';"><span>His</span> Dark Materials</h1>

<style>
  .element {
    --top: 20%;
    --bottom: 80%;
    --gap: 1rem;
    --offset: calc(var(--gap) / 2);
  }

  .element::before {
    clip-path: polygon(
      calc(var(--top) + var(--offset)) 0,
      100% 0,
      100% 100%,
      calc(var(--bottom) + var(--offset)) 100%
    );
  }

  .element::after {
    clip-path: polygon(
      calc(var(--top) - var(--offset)) 0,
      calc(var(--bottom) - var(--offset)) 100%,
      0 100%,
      0 0
    );
  }
</style>

his-dark-materials

#交错动画

如果我们想要为一些子元素添加交错动画,我们可以通过简单声明自定义属性为每个元素优雅地设置不同的 animation-delay 值:

<div class="el"></div>
<div class="el"></div>
<div class="el"></div>

<style>
  .el {
    --delay: calc(var(--i, 0) * 500ms);
    animation: fadeIn 1000ms var(--delay, 0ms);
  }

  .el:nth-child(2) {
    --i: 2;
  }

  .el:nth-child(3) {
    --i: 3;
  }
</style>
具体效果查看 codepen.io

无奈的是我们当前不得不分配固定数量的变量,因为如果我们有不定数量的子元素就会有问题(无法分别声明延时值)。Splitting JS 是一个很棒的 JavaScript 库,其可以分配元素索引作为变量,在这种交错动画中非常有用。但是这导致不得不使用 JS。

Adam Argyle 最近提交了一个提案,该提案提出两个新的 CSS 功能 sibling-count()sibling-index(),这将是一个改变游戏规则,使得一大堆的可能的 CSS 新的东西。目前,它们还远未被任何浏览器所采用,但这将是一个非常强大的功能,因此请密切注意。

#响应式布局网格

之前写过关于响应式网格的博客文章,但是自定义属性可以帮助我们使复杂布局更容易管理。假设我们有一个 8 列的网格,现在我们想要更改为标准的 12 列网格:

:root {
  --noOfColumns: 8;
}

@media (min-width: 60em) {
  :root {
    --noOfColumns: 12;
  }
}

.grid {
  display: grid;
  grid-template-columns: repeat(var(--noOfColumns), 1fr);
}

每当我们想要更新列数时,我们都不需要写入整个属性值 — 我们可以使用自定义属性。这是一个相对简单的示例,但是如果我们有一个更复杂的网格,它可能会更加有用。而且该技术也可以应用于轨道尺寸或物品放置位置。

#浏览器前缀

在某些浏览器中一些属性(如 clip-path)仍需要添加浏览器前缀 — 尽管值得庆幸的是,这个数字正在下降。如果需要编写浏览器前缀,然后要更改属性值,则还需要确保在前缀属性上也进行更改。使用自定义属性,我们可以改写为:

.some-element {
  --clip: polygon(0 0, 100% 0, 50% 100%, 0 100%);

  -webkit-clip-path: var(--clip);
  clip-path: var(--clip);
}

现在我们只有一个地方需要更改。

#总结

这些远非自定义属性的唯一用途,但我通常会发现它们是我在工作流程中所追求的,并且可以帮助你提高代码的效率和可维护性。毫无疑问,你将发现自己的更多用途!