color-scheme 属性用于指明您的网页支持哪些配色方案(例如浅色或深色)。这会告知浏览器,它可以自动为原生界面元素(例如滚动条、表单控件和默认画布背景)设置主题,以匹配您网站的设计,并有助于在初始加载期间最大限度地减少白色闪烁。
实现
1. 在 HTML 中声明支持的架构
强制性:为帮助防止“无主题内容闪烁”(FOUC),请在 <head> 中放置一个 <meta> 标记,以确保浏览器在开始渲染之前就知道您支持哪些主题。虽然此 <meta> 标记通过尽早设置初始画布颜色来帮助避免 FOUC,但它可能无法在所有浏览器或加载条件下完全消除闪烁。
<!-- MANDATORY: Declare support for both light and dark themes -->
<meta name="color-scheme" content="light dark">
2. 将页面级配色方案应用于 CSS :root 或 html
强制性:将 color-scheme 属性应用于 html 元素或 :root 伪类。浏览器会专门查看根元素,以确定整个视口的主题,包括根滚动条和初始“画布”背景。如果仅应用于 body,这些全局界面表面可能会保持浅色模式,因为 body 无法控制窗口的渲染上下文。
/* MANDATORY: Apply color-scheme to :root or html for viewport-wide theming */
:root {
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
}
3. 定义浅色和深色颜色令牌
您可以使用 light-dark() 函数定义自动适应不同 color-scheme 值的颜色令牌。
建议您还将原始颜色值保留在单独的自定义属性中,这样可以更轻松地以不同方式组合它们(并在需要时更轻松地实现后备行为)。
为了更好地控制 accent-color 或 scrollbar-color 等内置界面的颜色,作者可以选择使用自定义属性和/或 light-dark() 函数添加自己的动态颜色。此函数会根据计算出的元素 color-scheme 自动选择正确的颜色,从而避免了冗余的媒体查询,但基本实现不需要此函数。
:root {
--color-brand-light: oklch(45% 0.23 270);
--color-brand-dark: oklch(85% 0.15 210);
--color-brand-text-light: white;
--color-brand-text-dark: oklch(40% 0.23 270);
--color-brand: light-dark(var(--color-brand-light), var(--color-brand-dark));
--color-brand-text: light-dark(var(--color-brand-text-light), var(--color-brand-text-dark));
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
}
button.primary {
/* These automatically adapt to color scheme */
background-color: var(--color-brand);
color: var(--color-brand-text);
}
可选:有许多系统颜色可供使用,这些颜色也会自动适应所使用的配色方案(以及其他颜色模式,例如强制颜色),例如 canvas、canvastext、accentcolor(检查支持情况)、buttonborder 等。这些颜色通常过于有限,除了需要与某些默认浏览器界面完全匹配或作为后备/默认颜色的极少数情况外,其他情况下用处不大。
可选:根据上下文调整颜色对
即使替换系统默认设置,使用 prefers-color-scheme 媒体查询来定义不同的颜色对也很有用,这些颜色对会考虑页面周围的浏览器和操作系统 Chrome 的颜色(或周围页面的颜色,当页面用作 iframe 时)。
例如,当系统设置为 dark 时,使用稍微暗一点的浅色主题;当系统设置为 light 时,使用对比度更高的深色主题,这样页面就不会在视觉上被周围的界面所压制。
精细的浏览器界面自定义
设置 color-scheme 已经可以使浏览器界面适应所用的配色方案,但这会使用可能无法与网站设计完美契合的操作系统默认设置和/或系统颜色。
现代浏览器为这些功能提供了多个精细的自定义钩子。
在未充分利用现代浏览器提供的自定义钩子的情况下,请勿仅为了自定义原生控件的外观而重新实现它们。
设置强调色
某些浏览器界面(例如选中的复选框或滑块)会使用强调色。
默认情况下,此属性会解析为操作系统设置,但您可以使用 accent-color 属性将其设置为与页面更协调的颜色,例如页面的品牌颜色。
html {
accent-color: light-dark(var(--color-accent-light), var(--color-accent-dark));
}
使用 accent-color 时需要注意的问题
accent-color 的浏览器呈现方式各不相同。
对于通常绘制在强调色背景上的指示器(例如复选框标记和单选按钮圆点),浏览器可能会采用不同的处理方式:
- Chromium 和 Firefox 通常会选择对比鲜明的指示器颜色。
- Safari 可能会在浅色模式下保留白色指示器,或在深色模式下保留黑色指示器,同时修改其背后的强调色。这种调整并不总是能为每种提供的颜色提供足够的对比度,因此请在 Safari 中测试重要的组合,包括浅色和深色配色方案。不要假设 Safari 会自动选择对比度最佳的指示器颜色。
滚动条颜色
您可以将 scrollbar-color 与 light-dark() 结合使用,设置可适应所用配色方案的自定义滚动条颜色。
:root {
--color-scrollbar-track: light-dark(#eee, #222);
--color-scrollbar-thumb: light-dark(#999, #666);
scrollbar-color: var(--color-scrollbar-thumb) var(--color-scrollbar-track);
}
使用 scrollbar-color 时需要注意的问题
- 请勿为
scrollbar-color添加动画或过渡效果。WebKit bug 会导致滚动条在每次scrollbar-color发生变化时闪烁。 - 在 macOS 上,系统会默认忽略
scrollbar-color(标准)和::-webkit-scrollbar(旧版)属性,因为 macOS 使用原生“叠加”滚动条。您必须将自定义颜色与scrollbar-width(例如thin或auto)配对,以强制 macOS 呈现这些颜色。 - 即使应用了
scrollbar-width,macOS 叠加滚动条默认也会将轨道(边距)呈现为透明。如果设计要求在 macOS 上显示可见的滑轨背景颜色,您必须将scrollbar-gutter: stable;应用于可滚动容器,但请注意,该颜色仅在用户将鼠标悬停在滚动条上时才会显示。 - 即使使用
scrollbar-gutter: stable,轨道在 macOS 上也可能是透明的。滑块不应依赖于滑轨颜色来显示。
进一步自定义
大多数浏览器界面都会公开伪元素,以便完全自定义其外观,例如:
::placeholder::spelling-error::grammar-error::selection::search-text::target-text::file-selector-button
您可以在其中任何一个元素上使用 light-dark() 颜色,以应用与所用配色方案相适应的颜色。
4. 浅色模式和深色模式下的不同图片
对于 CSS 图片,light-dark() 也可用于提供不同的图片值。
重要提示:light-dark() 中的两个值必须属于同一类型。您无法将颜色与图片组合在一起。
如果其中一个必须是图片,请使用 image(<color>) 封装颜色值。
基于 JS 的主题检测
大多数配色方案分支都应在 CSS 中完成,CSS 会自动适应更改。
任何读取 matchMedia("(prefers-color-scheme: dark)").matches 的 JS 还必须处理其 change 事件,因为系统偏好设置可能随时发生变化。
可选:实现配色方案切换开关
如果您想允许用户为此网站手动覆盖系统默认设置,可以实现深色模式切换开关。 大多数网站不需要在网站界面中提供永久性切换开关。 通常,只需默认使用系统配色方案即可,也可以在单独的设置界面中进行设置。 如需了解有关实现深色模式切换开关(永久性或非永久性)的最佳实践,请参阅深色模式切换开关。
可选:组件专用替换
您可以通过为特定元素设置 color-scheme 来替换全局主题。
如需了解详情和注意事项,请参阅组件专用浅色/深色主题。
需要注意的已知问题
使用 color-scheme 时需要注意的问题
- Chrome 和 Firefox 会遵循 iframe 的
color-scheme:它们会以正确的配色方案呈现嵌入式网页,并调整嵌入式网页的prefers-color-scheme媒体查询,以反映嵌入上下文的color-scheme。Safari 则不会,即使在 iframe 内,也会将prefers-color-scheme解析为系统设置。- 如果您同时控制父级和 iframe:请在 iframe 构建时通过网址参数 (
?theme=dark) 或通过postMessage()(还可让您对运行时更改做出响应)将父级的配色方案明确传递给 iframe。在 iframe 中,根据该信号在<html>上设置一个类(和/或在:root上设置color-scheme),而不是依赖于prefers-color-scheme。 - 如果您只能控制嵌入式网页:在 Safari 中,无法通过 iframe 可靠地检测嵌入上下文的
color-scheme。在嵌入 API 上公开明确的主题参数(例如查询字符串或postMessage协议),并为嵌入者提供相关文档。
- 如果您同时控制父级和 iframe:请在 iframe 构建时通过网址参数 (
后备策略
color-scheme 的后备方案和浏览器支持
color-scheme 属性是渐进增强。不支持此属性的浏览器会忽略它,并使用其默认的浅色模式界面。
如需在旧版浏览器中适应用户的偏好设置,请使用 prefers-color-scheme 媒体查询,在用户偏好深色模式时提供不同的颜色。
- 请使用媒体查询在
:root或html上切换自定义属性 - 避免在单个组件上使用媒体查询,除非该组件需要非常特定的深色模式自定义(颜色除外)。
:root {
/* Define brand colors for each mode */
--color-brand-light: #0056b3;
--color-brand-dark: #00e5ff;
--color-brand: var(--color-brand-light);
/* MANDATORY: Fallback for browsers without light-dark support */
@media (prefers-color-scheme: dark) {
--color-brand: var(--color-brand-dark);
}
/* Ignored in older browsers */
color-scheme: light dark;
}
button.primary {
background-color: var(--color-brand);
}
light-dark() 的回退和浏览器支持
对于支持 color-scheme 但尚不支持 light-dark() 的浏览器,应首先将颜色的浅色和深色版本定义为自定义属性,并使用 prefers-color-scheme 媒体查询为相应模式设置颜色,如以下示例所示:
:root {
/* Define browser UI accent color for each mode */
--brand-accent-light: #0056b3;
--brand-accent-dark: #00e5ff;
--accent-color: var(--brand-accent-light);
/* MANDATORY: Fallback for browsers without light-dark support */
@media (prefers-color-scheme: dark) {
--accent-color: var(--brand-accent-dark);
}
/* OPTIONAL: use light-dark() for more control of built-in UI colors */
@supports (color: light-dark(white, black)) {
--accent-color: light-dark(var(--brand-accent-light), var(--brand-accent-dark));
}
/* MANDATORY: Automatically adapt native UI to user system preferences */
color-scheme: light dark;
/* Example inherited color property */
accent-color: var(--accent-color);
}
pre, code {
color-scheme: dark;
/* **Mandatory**: any inherited color properties must be set again, even if to the same design tokens */
accent-color: var(--accent-color);
}
light-dark() 图片值的后备和浏览器支持
对于不支持对图片使用 light-dark() 的浏览器,请使用 image-set() 作为开关:为每个选项提供一个来自自定义属性的 type(),并将非活跃选项设置为不受支持的 MIME 类型,以便浏览器舍弃该选项。
active 选项的值为空,因此它是一个不带 type() 的普通 image-set() 选项。
在 :root 上定义一次开关。然后,每个使用网站只需要内嵌这两个图片,例如 light-dark()。
:root {
--light-type: ;
--dark-type: type("image/do-not-use");
/* MANDATORY: Fallback for browsers without light-dark() image support */
@media (prefers-color-scheme: dark) {
--light-type: type("image/do-not-use");
--dark-type: ;
}
}
.hero {
background-image: image-set(url(hero-light.png) var(--light-type), url(hero-dark.png) var(--dark-type));
@supports (background-image: light-dark(url("a"), url("b"))) {
background-image: light-dark(url(hero-light.png), url(hero-dark.png));
}
}
- 系统只会下载所选图片。
- 解决方法仍然有效:
image-set(url(a-1x.png) 1x var(--light-type), url(a-2x.png) 2x var(--light-type), url(b-1x.png) 1x var(--dark-type), url(b-2x.png) 2x var(--dark-type))。 - 请勿将
type("")用于活动选项。浏览器会将空字符串视为不受支持,并会丢弃每个选项。 - 与
light-dark()不同,该开关不遵循在子树上设置的color-scheme。手动切换开关或设置color-scheme的任何子树也必须切换--light-type和--dark-type。
替代方案:每张图片一个自定义属性
当只有少数图片发生变化时,这种方法更简单,但每张图片都需要有自己的媒体查询分支,并且必须手动切换每个分支。
:root {
--hero-image: url(hero-light.png);
@media (prefers-color-scheme: dark) {
--hero-image: url(hero-dark.png);
}
@supports (background-image: light-dark(url("a"), url("b"))) {
--hero-image: light-dark(url(hero-light.png), url(hero-dark.png));
}
}
.hero {
background-image: var(--hero-image);
}
image() 的后备和浏览器支持
使用具有单个色标且没有位置的渐变,例如 linear-gradient(blue)。
为了获得更广泛的浏览器支持,您可以为单个颜色停止点明确提供 0 100% 位置,例如 linear-gradient(blue 0 100%)。
scrollbar-color 的后备方案和浏览器支持
此功能属于渐进增强,并不总是需要后备。
如果样式很重要,并且用户的基准目标是“基准广泛可用”或更早版本,则您应添加非标准 ::-webkit-scrollbar 伪元素作为后备。
将旧版后备样式封装在 @supports not (scrollbar-color: auto) 代码块中,以防止在原生支持标准属性和旧版 WebKit 选择器的浏览器中发生冲突。
如果您使用自定义属性来定义颜色,这些属性会自动级联到旧版 WebKit 选择器。您无需复制这些内容。
/* Legacy fallback for WebKit/Blink browsers */
@supports not (scrollbar-color: auto) {
.scroller::-webkit-scrollbar {
/* Must define base size in WebKit for custom colors to be visual */
width: 12px;
height: 12px;
}
.scroller::-webkit-scrollbar-thumb {
background: var(--scrollbar-thumb);
}
.scroller::-webkit-scrollbar-track {
background: var(--scrollbar-track);
}
}
accent-color 的回退和浏览器支持
accent-color 属性是渐进增强。
不支持此属性的浏览器会忽略它,并使用其默认界面颜色。