使用自定义属性、HSL 和少量 calc() 创建颜色主题

Avatar of Dieter Raber
Dieter Raber

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

CSS 自定义属性(我们可能在本文中将其称为“变量”,因为这就是它们的本质)出现之前,在同一个网站上实现多个颜色方案通常意味着编写单独的样式表。这绝对不是世界上最易于维护的东西。然而,如今,我们可以在单个样式表中定义变量,并让 CSS 完成神奇的运算。

即使您没有提供诸如用户生成或用户选择的颜色主题之类的功能,您仍然可以在您的网站上使用主题的概念。例如,在网站的不同区域使用不同的颜色主题相当常见。

我们将构建一个类似于此的示例

相同的布局,不同的颜色。

在这个示例中,各部分之间唯一变化的是颜色**色调**;亮度的变化始终相同。以下是一个特定色调的简化色板示例

多个色调的色板可能看起来像这样

使用 RGB 颜色值来实现这将需要付出努力,但在 HSL 中,只有一个值发生变化。

输入自定义属性

自定义属性已经存在了一段时间,并且得到了广泛支持填充其他解决方案 可用于 IE 11。

语法与传统 CSS 非常相似。以下是对基本用法的概述

通常在:root伪元素上定义变量,该元素在 HTML 中始终是<html>,但具有更高的特异性。也就是说,变量可以在任何元素上定义,这对于将特定变量的作用域限定到特定元素很有用。例如,以下是在数据属性上定义的变量

将 calc() 加入组合

变量不必是固定值。我们可以利用calc() 函数的功能,在遵循统一模式的同时自动为我们计算值

由于 CSS 不支持循环,预处理器将有助于生成一部分代码。但请记住:CSS 变量与 Sass 变量不同。

实现 CSS 变量

我们基本上要做的是在同一页面不同部分的同一个组件上更改颜色。就像这样

我们在选项卡中具有三个部分,它们有自己的 ID:#food#lifestyle#travel。每个部分对应于不同的色调。div.wrapper 元素上的data-theme-attribute 定义了当前使用的色调。

#travel 是活动选项卡时,我们使用的是--first-hue 变量,其值为 180°。这就是在该部分上用作--hue 值的值,从而产生青绿色。

<div class="wrapper" data-theme="travel">
.wrapper[data-theme="travel"] {
  --hue: var(--first-hue);  /* = 180° = teal */
}

单击任何选项卡都会将 data-theme 属性更新为该部分的 ID,同时删除其散列(#)。这需要一点 JavaScript。这是 CSS 的优点之一(很多):它们可以使用 JavaScript 进行访问和操作。这与预处理器变量大不相同,预处理器变量在构建阶段编译成值,不再可以在 DOM 中访问。

<li><a href="#food">Food</a></li>
const wrapper = document.querySelector('.wrapper');
document.querySelector("nav").addEventListener('click', e => {
  // Get theme name from URL and ditch the hash
  wrapper.dataset.theme = e.target.getAttribute('href').substr(1);
})

渐进增强

当我们使用 JavaScript 时,我们应该注意用户可能禁用了 JavaScript 的情况。否则,我们的脚本(以及我们的 UI 扩展)将无法访问。此代码段确保即使在这些情况下,网站内容仍然可以访问

// progressive enhancement:
// without JavaScript all sections are displayed, the theme is only set when the page loads
wrapper.dataset.theme = wrapper.querySelector('section').id;

这仅仅允许选项卡向上滚动页面到相应的部分。当然,主题消失了,但提供内容更为重要。

虽然我选择使用单页面方法,但也可以将这些部分作为单独的页面提供,并在服务器端设置[data-theme]

另一种方法

到目前为止,我们假设颜色值呈线性变化,因此可以采用数学方法。但即使在仅部分符合此假设的情况下,我们仍然可以从相同概念中获益。例如,如果亮度遵循某种模式,但色调不遵循,我们可以像这样拆分样式表

<head>
  <style>
    :root {
      --hue: 260;
    }
  </style>
  <link rel="stylesheet" href="stylesheet-with-calculations-based-on-any-hue.css">
</head>

支持 Web 组件

Web 组件 是一个令人兴奋(且不断发展)的概念。想象一下,我们可以拥有可以在任何地方重复使用并根据具体情况进行主题化的封装组件,这真是令人心动。一个组件,多种上下文!

我们可以将 CSS 变量主题与 Web 组件一起使用。这需要我们使用host-context() 伪选择器。(感谢 habemuscode 向我指出这一点!)

:host-context(body[data-theme="color-1"]) {
  --shade-1: var(--outsideHSL);
}

总之…

使用 CSS 自定义属性对网站进行主题化比我们过去采用的变通方法要容易得多。它更易于维护(一个样式表)、更高效(更少的代码),并开辟了新的可能性(使用 JavaScript)。更不用说,当 CSS 自定义属性与 HSL 颜色和calc() 函数一起使用时,它们会变得更加强大。

我们只看了一个示例,其中我们可以根据组件使用的部分来更改组件的颜色主题。但再说一次,当我们开始涉及诸如让用户自行更改主题之类的功能时,这里还有更多机会——Chris 在这篇文章中探讨了这一主题