使用Web标准实现深色模式

Lobsters Hottest 工具

摘要

一篇关于使用Web标准(如color-scheme元标签和prefers-color-scheme媒体查询)在网站上实现深色模式的指南,并包含一个JavaScript切换开关,可尊重用户偏好并将其存储在localStorage中。

<p><a href="https://lobste.rs/s/d1hevp/dark_mode_with_web_standards">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/05 10:29

# 使用 Web 标准实现深色模式 来源:https://olliewilliams.xyz/blog/dark-mode/ 尊重用户的操作系统设置非常简单:在 CSS 中使用 `prefers-color-scheme` 媒体查询即可。不过,这或许还不够:用户还应该能够在每个网站自行定制选择。例如,用户可能希望应用界面使用深色模式,但在内容密集的网站上阅读长文时则偏好浅色模式。我们需要做到以下两点: 1. 当用户首次访问网站时,以系统设置为默认方案。 2. 允许用户通过应用内的切换控件覆盖系统设置。 网页的颜色方案可以通过文档 `<head>` 中的 HTML meta 标签设置,也可以通过 CSS 的 `color-scheme` 属性作用于 `html` 元素。在慢速连接下,CSS 加载可能需要时间,因此推荐使用 meta 标签。 当用户第一次访问你的网站时,通过设置 `<meta name="color-scheme" content="light dark">` 来尊重用户的系统偏好。 若要通过网页应用或站点中的控件覆盖操作系统设置,可使用 JavaScript 更新 `content` 属性的值:设为 `light` 强制浅色模式,设为 `dark` 强制深色模式,设回 `light dark` 则恢复为系统设置。 ```js const metaTag = document.querySelector('[name="color-scheme"]'); const savedScheme = localStorage.getItem("colorScheme"); if (savedScheme) {metaTag.setAttribute('content', savedScheme);} btnlight.addEventListener('click', function() { metaTag.setAttribute('content', 'light'); localStorage.setItem("colorScheme", "light"); }); btndark.addEventListener('click', function() { metaTag.setAttribute('content', 'dark'); localStorage.setItem("colorScheme", "dark"); }); btnsystem.addEventListener('click', function() { metaTag.setAttribute('content', 'light dark'); localStorage.removeItem("colorScheme"); }); ``` #### `color-scheme` 会影响哪些内容? - 通过 `light-dark()` CSS 函数设置的颜色、渐变或图片。 - 系统颜色,如 `Canvas` 和 `CanvasText`。 - 滚动条颜色。 - HTML 元素(如按钮)的默认颜色。 - iframe 样式(只要 iframe 文档通过 meta 标签选择启用)。 - 使用了 `light-dark()` 或 `prefers-color-scheme` 的 SVG。 #### `color-scheme` 不会影响哪些内容? `color-scheme` 与 `prefers-color-scheme` 媒体查询之间存在一个不太好的脱节。`prefers-color-scheme` 反映的是操作系统设置——无论 `color-scheme` 的值如何。如果你在页面内提供了一个实现深色模式的切换开关,那么你就无法采用 `prefers-color-scheme` 媒体查询。以下代码不会受到 `color-scheme` 的影响: ```css @media (prefers-color-scheme: dark) { .element { background-color: black; color: white; } } ``` 除了使用 `background-image` 之外,遗憾的是,并没有一个与 `<meta>` 元素等效、能引用 `color-scheme` 的方法。 有两个例外情况,`color-scheme` 会影响 `prefers-color-scheme` 媒体查询: - iframe - SVG 下面是两个展示相同文档的 iframe。iframe 内部的文档使用了 `prefers-color-scheme` 媒体查询。 ```html <iframe src="..."></iframe> <iframe src="..." style="color-scheme: dark;"></iframe> ``` 如以上示例所示,当父文档使用了 `color-scheme: dark` 时,`@media (prefers-color-scheme: dark)` 样式会被应用。同样的原理也适用于 SVG。下面是一个 `.svg` 文件的内容: ```svg <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"> <circle cx="50" cy="50" r="40" /> </svg> ``` ```css circle { fill: rgb(40,40,40); } @media (prefers-color-scheme: dark) { circle { fill: rgb(216, 216, 216); } } ``` CSS 规范最近已进行更新([链接](https://github.com/w3c/csswg-drafts/pull/13857#:~:text=This%20PR%20applies%20the%20resolution%20from%20the%20recent%20F2F%20in%20#13377%2C%20making%20(prefers%2Dcolor%2Dscheme)%20reflect%20the%20effects%20of%20%3Cmeta%20name=color%2Dscheme%3E)),使得文档的 `color-scheme` 在所有上下文中都会影响媒体查询,但目前还没有浏览器实现这一改动。 #### Safari 的一些注意事项 - 在 SVG 中支持 `prefers-color-scheme` 媒体查询的功能已添加到 **Safari 27**([链接](https://developer.apple.com/documentation/safari-release-notes/safari-27-release-notes?changes=la,la#:~:text=175598175)-,Fixed%20an%20issue%20where,the%20system%20color%20appearance,-.%20(176413340)),但 `color-scheme` 不会影响该媒体查询(参见 bug 报告:[链接](https://bugs.webkit.org/show_bug.cgi?id=316640))。 - 在 iframe 中支持 `prefers-color-scheme` 媒体查询的功能已添加到 **Safari 27**([链接](https://developer.apple.com/documentation/safari-release-notes/safari-27-release-notes?changes=la,la#:~:text=140674753)-,Fixed%20an%20issue%20where,was%20set%20to%20dark,-.%20(142072593)),并且父文档的 `color-scheme` 会正确覆盖它,🎉。但其他 bug 仍然存在(参见 bug 报告:[链接](https://bugs.webkit.org/show_bug.cgi?id=316680))。 ## 在图片和渐变中使用 `light-dark()` `light-dark()` 函数最初仅限于颜色。现已可用于渐变和图片(自 Chrome/Edge 版本 150、Firefox 版本 150 及 Safari Technology Preview 起)。 ```css .bg-gradient { background-image: light-dark(linear-gradient(15deg, #b9b6ff, #308dc6), linear-gradient(15deg, #6b7495, #001339)); } ``` ```html <div class="bg-gradient">...</div> ``` 还可以根据 `color-scheme` 在单一纯色和渐变之间切换。 ```css .bg-grad-solid { background-image: light-dark(linear-gradient(15deg, #b9b6ff, #308dc6), image(#001339)); } ``` ### 图片 ```css .bg { background-image: light-dark(url(/lightmode.avif), url(/darkmode.avif)); } ``` ## 更改颜色、渐变和图片之外的内容 通常需要在不同模式之间更改的主要是颜色,但也有例外。例如,在深色模式下,`box-shadow` 可能在深色背景上不可见,因此你可能选择改用边框。目前实现这一点相当有挑战性。 CSS 标准组织正计划添加一种方法来检测当前颜色方案([链接](https://github.com/w3c/csswg-drafts/issues/10577#issuecomment-3329616811)),可能通过 CSS 的 `if()` 语句或样式查询来实现,但尚未有浏览器实现此功能。 我们可以拼凑出一种变通方法。下面来看两种方式。 定义一个 CSS 变量,当页面使用深色模式时设为 true: ```css html { --dark: false; } html:has([content="light dark"]) { @media (prefers-color-scheme: dark) { --dark: true; } } html:has([content="dark"]) { --dark: true; } ``` 现在所有浏览器都支持样式查询,你可以根据自定义属性的值应用样式: ```css @container style(--dark: false) { .card { box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.2); } } @container style(--dark: true) { .card { border: solid 1px rgb(94, 94, 94); } } ``` 一个更好的方法同样使用样式查询: ```css @property --usedScheme { syntax: "<color>"; inherits: true; initial-value: transparent; } body { --usedScheme: light-dark(white, black); } @container style(--usedScheme: white) { .card { box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.2); } } @container style(--usedScheme: black) { .card { border: solid 1px rgb(94, 94, 94); } } ``` ## (可能的)未来:通过 JavaScript 覆盖 `prefers-color-scheme` 未来我们或许能通过 JavaScript 来覆盖 `prefers-color-scheme` 媒体查询。目前有相关规范([链接](https://drafts.csswg.org/mediaqueries-5/#script-control-user-prefs))、MDN 条目([链接](https://developer.mozilla.org/en-US/docs/Web/API/User_Preferences_API))和 Chrome Canary 中的原型,但 Safari 团队反对这一想法([链接](https://github.com/WebKit/standards-positions/issues/252))。

相似文章

深色模式的六个层次(2024)

Hacker News Top

一篇探讨网页设计中深色模式实现的六个层次的文章,范围从基础的meta标签方法到使用CSS的高级配色方案切换技术。

仅用CSS指定每主题颜色的几种方法

Lobsters Hottest

本文介绍了五种仅用CSS实现每主题颜色(浅色/深色/自动)的技术,无需JavaScript,利用prefers-color-scheme、:has()、CSS变量和color-mix()等特性。

算法主题引擎

Hacker News Top

本文介绍了新的CSS `contrast-color()`函数,该函数允许开发人员自动选择黑色或白色文本,以实现与任何背景颜色的可访问对比度,解决网络上长期存在的低对比度问题,而无需依赖JavaScript。

readable.css

Lobsters Hottest

readable.css 是一个CSS框架,为网站提供了合理且美观的基础默认样式,支持亮/暗模式、响应式设计和垂直节奏,强调一致性和语义化HTML。