CSS 自定义属性和主题

Avatar of Chris Coyier
Chris Coyier

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

我们不久前发布 了关于原生 CSS 变量(自定义属性)和预处理器变量之间区别的文章。 预处理器变量可以做一些原生变量做不到的深奥的事情,但总的来说,原生变量可以做同样的事情。 但是,由于它们的实时插值方式,它们更强大。 如果它们的值发生变化(例如 JavaScript、媒体查询命中等),更改会立即触发网站上的更改。

很酷,对吧? 但仍然,这实际上有多有用? 主要用例是什么? 我认为我们仍在努力找出答案。

我想,一个用例是网站主题(比如:网站周围元素的自定义颜色)。 而不是为一堆不同的主题编写不同的 CSS,或者编写 JavaScript 来定位我们要更改的所有元素并更改它们,我们只需编写一组基本 CSS 来使用变量,并将这些变量设置为主题颜色。

想象一下,我们允许自定义网站的页眉和页脚背景。

header {
  background: var(--mainColor);
}

...

footer {
  background: var(--mainColor);
}

可能有一个子标题,使用该颜色的深色变体。 以下是一个在另一个颜色之上叠加透明颜色层的技巧

.subheader {
  background: 
    /* Make a bit darker */
    linear-gradient(
      to top,
      rgba(0, 0, 0, 0.25),
      rgba(0, 0, 0, 0.25)
    )
    var(--mainColor);
}

--mainColor 从哪里来?

使用主题,想法是您向用户询问它。 幸运的是,我们有颜色输入

<input type="color">

您可以将该值存储在数据库或您想要的任何其他存储机制中。 这是一个小的演示,其中该值存储在 localStorage 中

该值从 localStorage 中取出,并在页面加载时使用。 如果该值不存在,还会设置一个默认值(在 CSS 中)。

对我来说,上面的演示之所以引人入胜,是因为代码量很少。 将其维护为网站上的功能主要是 CSS 工作,并且似乎足够灵活,可以经受住时间的考验(可能)。

不出所料,我在这个方面落后了。

许多人认为主题是 CSS 自定义属性的主要用例之一。 让我们看看其他一些人的示例。

Giacomo Zinetti 有类似的颜色选择器实现

在他的网站上:

Harry Roberts 的示例和建议

他写了“使用自定义属性进行务实、实用和渐进式主题设计”,他在其中指出了像 Twitter 和 Trello 这样的应用程序,它们直接为用户提供主题设计

Harry 做了很多咨询工作,令我惊讶的是,他发现自己经常与想要这样做很多的企业合作。 他警告说

在大多数情况下,主题设计完全是一个锦上添花的功能。 它不是业务关键,通常甚至不重要。 如果有人要求您提供这样的主题设计,请不要以牺牲性能或代码质量为代价来实现它。

在 Sass 中 / 在 React 中

在通过自定义属性进行主题设计的真实应用程序中,Dan Bahrami 讲述了他们如何在 Geckoboard 上做到的,这是他正在开发的产品

这是一个 React 产品,但他们没有使用任何 JavaScript 中的样式,因此他们选择使用自定义属性(通过 Sass)来进行主题设计。

@mixin variable($property, $variable, $fallback) {
  #{$property}: $fallback;
  #{$property}: var($variable);
}

因此,他们可以执行以下操作

.dashboard {
  @include variable(background, --theme-primary-color, blue);
}

这编译成具有回退

.dashboard {
  background: blue;
  background: var(--theme-primary-color);
}

他们还创建了 react-custom-properties,它完全是关于将自定义属性应用于组件,利用了您可以将自定义属性设置为内联样式的事实

<div style="--theme-primary-color: blue;">

</div>

不止一种颜色和属性

不仅颜色可以更改,自定义属性可以是任何有效值。 以下是由 Keith Clark 提供的演示,其中包含多种颜色和字体大小

David Darnes 在 Jekyll 网站中内置了主题设计

Microsoft 演示

Greg Whitworth 创建了这个演示(现在已下线,抱歉)

它使用颜色函数本身中的颜色修饰符

.distant-building__window {
  fill: rgb(
    calc(111 + (111 * var(--building-r-mod))),
    calc(79 + (79 * var(--building-g-mod))),
    calc(85 + (85 * var(--building-b-mod)))
  );
}

这在所有浏览器中都不受支持。

Greg 还指出,CSS4 颜色函数(我们之前讨论过)将使所有这些主题设计功能更加强大。

Polymer 项目通过自定义属性进行主题设计

至少它在 v1 文档中是这样做的。 想法是,您将拥有一个 Web 组件,例如

<iron-icon icon="[[toggleIcon]]">
</iron-icon>

它具有智能默认值,但专门构建为允许通过主题进行样式设计

<style>
  iron-icon {
    fill: var(--icon-toggle-color, rgba(0,0,0,0));
    stroke: var(--icon-toggle-outline-color, currentcolor);
  }
  :host([pressed]) iron-icon {
    fill: var(--icon-toggle-pressed-color, currentcolor);
  }
</style>

这意味着您可以设置这些变量,并使该组件采用新的颜色。

支持和回退

最近的支持情况已经相当不错了

此浏览器支持数据来自 Caniuse,该网站提供了更多详细信息。 数字表示浏览器在该版本及更高版本中支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
49311610

移动设备/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
12712712710.0-10.2

Opera Mini 和 IE 尤其缺失。 我们已经讨论过在使用自定义属性之前设置有效的非变量属性作为回退的想法。

就像许多现代 CSS 功能一样,您可以使用 @supports 在使用之前测试支持情况

@supports (--color: red) {
  :root {
    --color: red;
  }
  body {
    color: var(--color);
  }
}

这始终取决于具体情况,但在 CSS 中,将回退放在之前的声明上可能是处理不支持情况最有效的方法。 还有边缘情况

Michael Scharnagl 文档 中记录了一种用于测试的 JavaScript 方法

if (window.CSS && window.CSS.supports && window.CSS.supports('--a', 0)) {
  // CSS custom properties supported.
} else {
  // CSS custom properties not supported
}

颜色和无障碍

在为文本设置颜色以及文本后面的颜色时,这些颜色之间的对比度是一个无障碍问题。 对比度过低,难以阅读。

一个比较常见的解决方案是根据后面的颜色来选择文本是浅色还是深色(白色或黑色)。

David Halford 使用 JavaScript 演示了如何计算此对比度

Brendan Saunders 使用 Sass 演示了如何计算此对比度