通过配色方案 CSS 属性和相应的元标记改进了深色模式默认样式

借助 color-scheme CSS 属性和相应的元标记,开发者可以选择让自己的网页采用用户代理样式表的主题特定默认设置。

背景

prefers-color-scheme 用户偏好媒体功能

借助 prefers-color-scheme 用户偏好设置媒体功能,开发者可以完全控制网页的外观。 如果您不熟悉深色模式,请阅读我的文章prefers-color-scheme:Hello darkness, my old friend,其中记录了我所了解的有关打造出色深色模式体验的所有信息。

文章中仅简要提及的一个拼图块是 color-scheme CSS 属性和同名的相应元标记。它们都允许您选择将网页纳入用户代理样式表的特定于主题的默认设置(例如表单控件、滚动条以及 CSS 系统颜色),从而让您作为开发者更轻松地完成工作。与此同时,此功能可防止浏览器自行应用任何转换。

浏览器支持

prefers-color-scheme

Browser Support

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 67.
  • Safari: 12.1.

Source

color-scheme

Browser Support

  • Chrome: 81.
  • Edge: 81.
  • Firefox: 96.
  • Safari: 13.

Source

用户代理样式表

在继续之前,我先简要介绍一下什么是用户代理样式表。在大多数情况下,您可以将“用户代理”(UA) 一词视为“浏览器”的另一种说法。UA 样式表决定了网页的默认外观和风格。 顾名思义,UA 样式表是指依赖于相关 UA 的样式表。您可以查看 Chrome(和 Chromium)的 UA 样式表,并将其与 FirefoxSafari(和 WebKit)的 UA 样式表进行比较。 通常,UA 样式表在大多数方面都保持一致。例如,它们都会将链接设为蓝色、将常规文本设为黑色、将背景颜色设为白色,但也有一些重要(有时令人烦恼)的区别,例如它们设置表单控件样式的方式。

详细了解 WebKit 的 UA 样式表及其在深色模式方面的作用。 (在样式表中对“深色”进行全文搜索。) 样式表提供的默认值会根据深色模式的开启或关闭状态而变化。为了说明这一点,下面提供了一个使用 :matches 伪类和 WebKit 内部变量(如 -apple-system-control-background)以及 WebKit 内部预处理器指令 #if defined 的 CSS 规则示例:

input,
input:matches([type="password"], [type="search"]) {
  -webkit-appearance: textfield;
  #if defined(HAVE_OS_DARK_MODE_SUPPORT) &&
      HAVE_OS_DARK_MODE_SUPPORT
    color: text;
    background-color: -apple-system-control-background;
  #else
    background-color: white;
  #endif
  /* snip */
}

您会注意到,上述 colorbackground-color 属性有一些非标准值。text-apple-system-control-background 都不是有效的 CSS 颜色。 它们是 WebKit 内部的语义颜色。

事实证明,CSS 已将语义系统颜色标准化。它们在 CSS 颜色模块级别 4 中指定。 例如,Canvas(不要与 <canvas> 标记混淆)用于应用内容或文档的背景,而 CanvasText 用于应用内容或文档中的文本。 这两者应搭配使用,不应单独使用。

UA 样式表可以使用自己的专有语义系统颜色或标准化的语义系统颜色,来确定 HTML 元素的默认呈现方式。如果操作系统设置为深色模式或使用深色主题,CanvasText(或 text)会条件性地设置为白色,而 Canvas(或 -apple-system-control-background)会设置为黑色。然后,UA 样式表仅分配一次以下 CSS,并同时涵盖浅色和深色模式。

/**
  Not actual UA stylesheet code.
  For illustrative purposes only.
*/
body {
  color: CanvasText;
  background-color: Canvas
}

color-scheme CSS 属性

CSS 颜色调整模块级别 1 规范引入了一个模型,并控制用户代理对颜色进行自动调整,目的是处理用户偏好设置,例如深色模式、对比度调整或特定的所需配色方案。

其中定义的 color-scheme 属性允许元素指明它适合使用哪些配色方案进行渲染。这些值会根据用户偏好进行协商,从而确定所选的配色方案,该方案会影响用户界面 (UI) 元素,例如表单控件和滚动条的默认颜色,以及 CSS 系统颜色的使用值。 目前,系统支持以下值:

  • normal 表示元素完全不知道配色方案,因此应使用浏览器的默认配色方案来呈现元素。

  • [ light | dark ]+ 表示元素知道并可以处理列出的颜色方案,并表达了它们之间的有序偏好。

在此列表中,light 表示浅色配色方案,具有浅色背景颜色和深色前景色,而 dark 则表示相反的情况,具有深色背景颜色和浅色前景色。

对于所有元素,使用配色方案进行渲染应使元素的所有浏览器提供的界面中使用的颜色与配色方案的意图相符。例如,滚动条、拼写检查下划线、表单控件等。

:root 元素上,使用配色方案进行渲染还必须影响画布的表面颜色(即全局背景颜色)、color 属性的初始值和系统颜色的使用值,并且还应影响视口的滚动条。

/*
  The page supports both dark and light color schemes,
  and the page author prefers dark.
*/
:root {
  color-scheme: dark light;
}

color-scheme 元标记

遵循 color-scheme CSS 属性需要先下载 CSS(如果通过 <link rel="stylesheet"> 引用),然后进行解析。为了帮助用户代理立即以所需的配色方案呈现网页背景,还可以在 <meta name="color-scheme"> 元素中提供 color-scheme 值。

<!--
  The page supports both dark and light color schemes,
  and the page author prefers dark.
-->
<meta name="color-scheme" content="dark light">

合并 color-schemeprefers-color-scheme

由于元标记和 CSS 属性(如果应用于 :root 元素)最终会产生相同的行为,因此我始终建议通过元标记指定配色方案,以便浏览器可以更快地采用首选方案。

虽然对于绝对基准网页,无需添加额外的 CSS 规则,但在一般情况下,您应始终将 color-schemeprefers-color-scheme 结合使用。例如,WebKit 和 Chrome 用于经典链接蓝色 rgb(0,0,238) 的专有 WebKit CSS 颜色 -webkit-link 在黑色背景上的对比度不足,仅为 2.23:1,不符合 WCAG AA 和 WCAG AAA 要求

我已经针对 ChromeWebKitFirefox 提交了 bug,并在 HTML 标准中提交了元问题,以解决此问题。

prefers-color-scheme 互动

color-scheme CSS 属性和相应的元标记与 prefers-color-scheme 用户偏好设置媒体功能的相互作用起初可能看起来令人困惑。事实上,它们搭配起来效果非常好。 最重要的是要了解,color-scheme 专门用于确定默认外观,而 prefers-color-scheme 用于确定可设置样式的外观。为了更清楚地说明这一点,我们假设有以下网页:

<head>
  <meta name="color-scheme" content="dark light">
  <style>
    fieldset {
      background-color: gainsboro;
    }
    @media (prefers-color-scheme: dark) {
      fieldset {
        background-color: darkslategray;
      }
    }
  </style>
</head>
<body>
  <p>
    Lorem ipsum dolor sit amet, legere ancillae ne vis.
  </p>
  <form>
    <fieldset>
      <legend>Lorem ipsum</legend>
      <button type="button">Lorem ipsum</button>
    </fieldset>
  </form>
</body>

网页上的内嵌 CSS 代码通常会将 <fieldset> 元素的 background-color 设置为 gainsboro,如果用户根据 prefers-color-scheme 用户偏好媒体功能偏好 dark 配色方案,则设置为 darkslategray

通过 <meta name="color-scheme" content="dark light"> 元素,网页会告知浏览器它支持深色主题和浅色主题,并且偏好深色主题。

根据操作系统是设置为深色模式还是浅色模式,整个网页会根据用户代理样式表以浅色显示在深色背景上,反之亦然。更改段落文本或网页背景颜色时,无需使用开发者提供的其他 CSS。

请注意,<fieldset> 元素的 background-color 会根据是否启用深色模式而变化,遵循网页上开发者提供的内嵌样式表中的规则。 值为 gainsborodarkslategray

浅色模式下的网页。
浅色模式:开发者和用户代理指定的样式。 根据用户代理样式表,文字为黑色,背景为白色。 根据内嵌的开发者样式表,<fieldset> 元素的 background-colorgainsboro
深色模式下的页面。
深色模式:开发者和用户代理指定的样式。 根据用户代理样式表,文字为白色,背景为黑色。 根据内嵌的开发者样式表,<fieldset> 元素的 background-colordarkslategray

<button> 元素的外观由用户代理样式表控制。 其 color 设置为 ButtonText 系统颜色,其 background-color 和四个 border-color 设置为 ButtonFace 系统颜色。

使用 ButtonFace 属性的浅色模式网页。
浅色模式background-color 和各种 border-color 均设置为 ButtonFace 系统颜色。

现在,请注意 <button> 元素的 border-color 如何变化。 border-top-colorborder-bottom-color计算值从 rgba(0, 0, 0, 0.847)(偏黑)变为 rgba(255, 255, 255, 0.847)(偏白),因为用户代理会根据配色方案动态更新 ButtonFace<button> 元素的 color 设置为相应的系统颜色 ButtonText 时,也是如此。

显示计算出的颜色值与 ButtonFace 相匹配。
浅色模式:用户代理样式表中均设置为 ButtonFaceborder-top-colorborder-bottom-color 的计算值现在为 rgba(0, 0, 0, 0.847)
显示计算出的颜色值在深色模式下仍与 ButtonFace 匹配。
深色模式:用户代理样式表中同时设置为 ButtonFaceborder-top-colorborder-bottom-color 的计算值现在为 rgba(255, 255, 255, 0.847)

演示

您可以在 Glitch 上的演示中查看 color-scheme 应用于大量 HTML 元素的效果。此演示版故意显示了 WCAG AA 和 WCAG AAA 违规情况,并使用了上述警告中提到的链接颜色。

浅色模式下的演示。
演示切换为 color-scheme: light
深色模式下的演示。
演示切换为 color-scheme: dark。 请注意,链接颜色存在 WCAG AA 和 WCAG AAA 违规问题

致谢

color-scheme CSS 属性和相应的元标记由 Rune Lillesveen 实现。Rune 还是 CSS 颜色调整模块级别 1 规范的共同编辑者。 主打图片由 Philippe Leone 拍摄,选自 Unsplash