实用 CSS 自定义属性使用模式

Avatar of Tyler Childs
Tyler Childs

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费信用额度!

我一直在使用 CSS 自定义属性 来探索它们的力量,因为浏览器支持终于达到了我们可以将其用于生产代码的程度。 我已经用多种不同的方式使用它们,并且希望你也能像我一样对它们感到兴奋。 它们非常有用且强大!

我发现 **CSS 变量的使用往往会归类。** 当然,你可以随意使用 CSS 变量,但是将它们考虑在这些不同的类别中可能会帮助你了解它们的不同使用方法。

  • 变量。 基本内容,例如设置品牌 color 以供在需要时使用。
  • 默认值。 例如,可以稍后覆盖的默认 border-radius
  • 级联值。 使用基于特异性的线索,例如用户偏好。
  • 作用域规则集。 对单个元素(如链接和按钮)的刻意变化。
  • 混合。 旨在将其值带入新上下文的规则集。
  • 内联属性。 从我们 HTML 中的内联样式中传入的值。

我们将要查看的示例是我创建和维护的 CSS 框架 Cutestrap 中简化和浓缩的模式。

关于浏览器支持的快速说明

当提到自定义属性时,我经常会听到两种常见的问题。 第一个问题是关于浏览器支持。 哪些浏览器支持它们? 在不支持它们的地方,我们需要使用哪些回退?

这篇文章中涵盖的内容在 全球市场份额 中的 85%。 不过,仍然值得将 caniuse) 与你的用户群进行交叉引用,以确定渐进增强在你的项目中有多大意义以及在哪些地方有意义。

第二个问题总是关于如何使用自定义属性。 所以让我们深入研究使用方法吧!

模式 1:变量

我们将要解决的第一件事是为品牌颜色设置一个自定义属性变量,并在 SVG 元素上使用它。 我们还将使用回退来覆盖尾随浏览器的用户。

html {
  --brand-color: hsl(230, 80%, 60%);
}

.logo {
  fill: pink; /* fallback */
  fill: var(--brand-color);
}

这里,我们在 html 规则集中声明了一个名为 --brand-color 的变量。 该变量是在始终存在的元素上定义的,因此它将级联到使用它的每个元素。 长话短说,我们可以在 .logo 规则集中使用该变量。

我们为尾随浏览器声明了一个 pink 回退值。 在第二个填充声明中,我们将 --brand-color 传递到 var() 函数中,该函数将返回我们为该自定义属性设置的值。

这几乎就是模式的运作方式:定义变量 (--variable-name),然后在元素上使用它 (var(--variable-name))。

查看 Pen
实用自定义属性模式:示例 1.0
by Tyler Childs (@tylerchilds)
CodePen 上。

模式 2:默认值

我们在第一个示例中使用的 var() 函数也可以提供默认值,以防它试图访问的自定义属性未设置。

例如,假设我们为按钮设置圆角边框。 我们可以创建一个变量(我们将其称为 --roundness),但我们不会像之前那样定义它。 相反,我们将在使用该变量时分配一个默认值。

.button {
  /* --roundness: 2px; */
  border-radius: var(--roundness, 10px);
}

在不定义自定义属性的情况下使用默认值的用例是,当你的项目仍在设计中,但你的功能 **今天到期** 时。 这使得以后在设计更改时更新值变得更容易。

因此,你为按钮提供了一个不错的默认值,满足了你的截止日期,当 --roundness 最终被设置为全局自定义属性时,你的按钮将免费获得该更新,而无需重新进行设置。

查看 Pen
实用自定义属性模式:示例 2.0
by Tyler Childs (@tylerchilds)
CodePen 上。

你可以在 CodePen 上进行编辑,并取消上面的代码注释,看看当设置了 --roundness 时按钮会是什么样子!

模式 3:级联值

现在我们已经掌握了基础知识,让我们开始构建我们欠自己的未来。 我真的很怀念 AIM 和 MySpace 通过让用户在个人资料上表达自定义文本和背景颜色来带来的个性。

让我们将它带回来,并构建一个学校留言板,让每个学生都可以为他们发布的留言设置自己的字体、背景颜色和文本颜色。

基于用户的主题

我们基本上是在让学生创建自定义主题。 我们将在数据属性规则集中设置主题配置,以便任何后代(在本例中为 .message 元素)使用这些主题都可以访问这些自定义属性。

.message {
  background-color: var(--student-background, #fff);
  color: var(--student-color, #000);
  font-family: var(--student-font, "Times New Roman", serif);
  margin-bottom: 10px;
  padding: 10px;
}

[data-student-theme="rachel"] {
  --student-background: rgb(43, 25, 61);
  --student-color: rgb(252, 249, 249);
  --student-font: Arial, sans-serif;
}

[data-student-theme="jen"] {
  --student-background: #d55349;
  --student-color: #000;
  --student-font: Avenir, Helvetica, sans-serif;
}

[data-student-theme="tyler"] {
  --student-background: blue;
  --student-color: yellow;
  --student-font: "Comic Sans MS", "Comic Sans", cursive;
}

这是标记

<section>
  <div data-student-theme="chris">
    <p class="message">Chris: I've spoken at events and given workshops all over the world at conferences.</p>
  </div>
  <div data-student-theme="rachel">
    <p class="message">Rachel: I prefer email over other forms of communication.</p>
  </div>
  <div data-student-theme="jen">
    <p class="message">Jen: This is why I immediately set up my new team with Slack for real-time chat.</p>
  </div>
  <div data-student-theme="tyler">
    <p class="message">Tyler: I miss AIM and MySpace, but this message board is okay.</p>
  </div>
</section>

我们使用 [data-student-theme] 选择器为我们的学生主题规则集设置了所有学生主题。 如果为该学生设置了 backgroundcolorfont 的自定义属性,它们将应用于我们的 .message 规则集,因为 .message 是包含数据属性的 div 的后代,而该 div 又包含要使用的自定义属性值。 否则,将使用我们提供的默认值。

查看 Pen
实用自定义属性模式:示例 3.0
by Tyler Childs (@tylerchilds)
CodePen 上。

可读主题覆盖

用户控制自定义样式固然有趣且酷炫,但用户选择的内容并不总是会考虑到对比度、色觉障碍或任何喜欢阅读时眼睛不流血的人的无障碍性。 还记得 GeoCities 的日子吗?

让我们添加一个类,提供更干净的外观和感觉,并将其设置在父元素 (<section>) 上,以便在存在该类时覆盖任何学生主题。

.readable-theme [data-student-theme] {
  --student-background: hsl(50, 50%, 90%);
  --student-color: hsl(200, 50%, 10%);
  --student-font: Verdana, Geneva, sans-serif;
}
<section class="readable-theme">
  ...
</section>

我们利用级联通过设置更高的特异性来覆盖学生主题,这样背景、颜色和字体将在范围内,并将应用于每个 .message 规则集。

查看 Pen
实用自定义属性模式:示例 3.1
by Tyler Childs (@tylerchilds)
CodePen 上。

模式 4:作用域规则集

说到作用域,我们可以对自定义属性进行作用域化,并使用它们来简化原本是样板 CSS 的内容。 例如,我们可以为不同的链接状态定义变量。

a {
  --link: hsl(230, 60%, 50%);
  --link-visited: hsl(290, 60%, 50%);
  --link-hover: hsl(230, 80%, 60%);
  --link-active: hsl(350, 60%, 50%);
}

a:link {
  color: var(--link);
}

a:visited {
  color: var(--link-visited);
}

a:hover {
  color: var(--link-hover);
}

a:active {
  color: var(--link-active);
}
<a href="#">Link Example</a>

现在我们已经全局地在 <a> 元素上写出了自定义属性,并在我们的链接状态上使用了它们,我们不再需要再次编写它们。 这些是作用于 <a> 元素的规则集,因此它们只设置在锚标签及其子元素上。 这使我们能够避免污染全局命名空间。

示例:灰度链接

展望未来,我们可以通过更改不同用例的自定义属性来控制我们刚刚创建的链接。例如,让我们创建一个灰色链接。

.grayscale {
  --link: LightSlateGrey;
  --link-visited: Silver;
  --link-hover: DimGray;
  --link-active: LightSteelBlue;
}
<a href="#" class="grayscale">Link Example</a>

我们声明了一个 .grayscale 规则集,其中包含不同链接状态的颜色。由于此规则集的选择器比默认选择器具有更高的特异性,因此这些变量值将被使用,并应用于链接状态的伪类规则集,而不是在 <a> 元素上定义的值。

查看 Pen
实用自定义属性模式:示例 4.0
由 Tyler Childs 创建 (@tylerchilds)
CodePen 上。

示例:自定义链接

如果设置四个自定义属性感觉工作量太大,我们是否可以设置一个色调值?这样可以更轻松地管理。

.custom-link {
  --hue: 30;
  --link: hsl(var(--hue), 60%, 50%);
  --link-visited: hsl(calc(var(--hue) + 60), 60%, 50%);
  --link-hover: hsl(var(--hue), 80%, 60%);
  --link-active: hsl(calc(var(--hue) + 120), 60%, 50%);
}

.danger {
  --hue: 350;
}
<a href="#" class="custom-link">Link Example</a>
<a href="#" class="custom-link danger">Link Example</a>

查看 Pen
实用自定义属性模式:示例 4.1
由 Tyler Childs 创建 (@tylerchilds)
CodePen 上。

通过引入色调值的变量,并将其应用于其他变量中的 HSL 颜色值,我们只需更改该值即可更新所有四个链接状态。

计算与自定义属性相结合非常强大,因为它们可以
使您的样式更加简洁高效。 查看此技巧 由 Josh Bader 创建,他使用类似的方法来强制按钮的无障碍颜色对比度。

模式 5:混入

就自定义属性而言,混入是声明为自定义属性值的函数。混入的参数是其他自定义属性,这些属性将在更改时重新计算混入,进而更新样式。

我们刚刚看到的自定义链接示例实际上是一个混入。我们可以设置 --hue 的值,然后所有四个链接状态都会相应地重新计算。

示例:基线网格基础

让我们通过创建一个基线网格来了解有关混入的更多信息,以帮助实现垂直节奏。这样,我们的内容通过利用一致的间距,可以实现令人愉悦的节奏。

.baseline,
.baseline * {
  --rhythm: 2rem;
  --line-height: var(--sub-rhythm, var(--rhythm));
  --line-height-ratio: 1.4;
  --font-size: calc(var(--line-height) / var(--line-height-ratio));
}

.baseline {
  font-size: var(--font-size);
  line-height: var(--line-height);
}

我们将基线网格的规则集应用于 .baseline 类及其所有后代。

  • --rhythm:这是我们基线的核心。更新它将影响所有其他属性。
  • --line-height:默认情况下,它被设置为 --rhythm,因为这里没有设置 --sub-rhythm
  • --sub-rhythm:这使我们能够覆盖 --line-height(以及随之而来的 --font-size),同时保持整体基线网格。
  • --line-height-ratio:这有助于在文本行之间强制执行适当的间距。
  • --font-size:这是通过将我们的 --line-height 除以我们的 --line-height-ratio 来计算的。

我们还在 .baseline 规则集中设置了我们的 font-sizeline-height,以使用我们基线网格中的 --font-size--line-height。简而言之,每当节奏发生变化时,行高和字号都会相应地发生变化,同时保持易读性。

好的,让我们使用基线。

让我们创建一个小型网页。我们将使用我们的 --rhythm 自定义属性来设置所有元素之间的间距。

.baseline h2,
.baseline p,
.baseline ul {
  padding: 0 var(--rhythm);
  margin: 0 0 var(--rhythm);
}

.baseline p {
  --line-height-ratio: 1.2;
}

.baseline h2 {
  --sub-rhythm: calc(3 * var(--rhythm));
  --line-height-ratio: 1;
}

.baseline p,
.baseline h2 {
  font-size: var(--font-size);
  line-height: var(--line-height);
}

.baseline ul {
  margin-left: var(--rhythm);
}
<section class="baseline">
  <h2>A Tiny Webpage</h2>
  <p>This is the tiniest webpage. It has three noteworthy features:</p>
  <ul>
    <li>Tiny</li>
    <li>Exemplary</li>
    <li>Identifies as Hufflepuff</li>
  </ul>
</section>

我们实际上在这里使用了两个混入:--line-height--font-size。我们需要将 font-sizeline-height 属性设置为它们的自定义属性对应项,才能设置标题和段落。这些混入在这些规则集中已经重新计算,但需要先设置它们,然后才能将更新后的样式应用于它们。

查看 Pen
实用自定义属性模式:示例 5.0
由 Tyler Childs 创建 (@tylerchilds)
CodePen 上。

需要记住的一点是:当使用通配符选择器应用混入时,**可能不希望**在规则集本身中使用自定义属性值。这会使这些样式的特异性高于级联带来的任何其他继承,使其难以覆盖,除非使用 !important

模式 6:内联属性

我们也可以内联声明自定义属性。让我们构建一个轻量级的网格系统来演示。

.grid {
  --columns: auto-fit;

  display: grid;
  gap: 10px;
  grid-template-columns: repeat(var(--columns), minmax(0, 1fr));
}
<div class="grid">
  <img src="https://www.fillmurray.com/900/600" alt="Bill Murray" />
  <img src="https://www.placecage.com/900/600" alt="Nic Cage" />
  <img src="https://www.placecage.com/g/900/600" alt="Nic Cage gray" />
  <img src="https://www.fillmurray.com/g/900/600" alt="Bill Murray gray" />
  <img src="https://www.placecage.com/c/900/600" alt="Nic Cage crazy" />
  <img src="https://www.placecage.com/gif/900/600" alt="Nic Cage gif" />
</div>

默认情况下,网格具有大小相同的列,这些列会自动排列成一行。

查看 Pen
实用自定义属性模式:示例 6.0
由 Tyler Childs 创建 (@tylerchilds)
CodePen 上。

要控制列数,我们可以设置我们的 --columns 自定义属性
内联到我们的网格元素上。

<div class="grid" style="--columns: 3;">
  ...
</div>

查看 Pen
实用自定义属性模式:示例 6.1
由 Tyler Childs 创建 (@tylerchilds)
CodePen 上。


我们刚刚介绍了六种不同的自定义属性用例(至少是我常用的用例)。即使您已经了解并一直在使用自定义属性,希望看到这些用法可以帮助您更好地了解何时何地有效地使用它们。

您是否在使用自定义属性时使用了其他类型的模式?在评论中分享并链接一些演示——我很想看看它们!

如果您不熟悉自定义属性,并且想提升水平,可以尝试使用我们在此处介绍的示例,但添加媒体查询。您将看到它们是如何自适应的,以及在您拥有动态更改值的强大功能时,有多少有趣的机会出现。

此外,CSS-Tricks 上还有大量其他出色的资源来提升您的自定义属性能力,请访问 自定义属性指南

查看 Pen
感谢您的阅读!
由 Tyler Childs 创建 (@tylerchilds)
CodePen 上。