为任何网站启用深色模式 —— 4 种 CSS 方案
你的操作系统多年前就有了深色模式。你的浏览器也支持它。大多数应用都遵守它。然后你打开一份文档、一个博客或一个 2014 年的内网 —— 晚上 11 点迎面被一片 100% 纯白背景糊一脸。四种方法,一劳永逸地解决它。
这个问题为什么存在
CSS 标准 prefers-color-scheme 自 2019 年起进入 Chrome、2018 年起进入 Safari。你的操作系统知道你的偏好,浏览器把它传递下去,网站本应遵守。本应 —— 而实际上约 40% 的网站对它视而不见。原因:祖传代码、没有预算做第二套主题、市场部想要「品牌一致性」、作者还没想到这茬。
如果作者不替你做 —— 那就自己来。四种方法,按优雅程度递增排列。
方法 1 —— 通用滤镜(对一切都管用)
一段代码,适配任何网站,无需分析具体的 CSS:
html {
filter: invert(0.92) hue-rotate(180deg);
background: #1a1a1a;
}
/* 把图片、视频和 iframe 再次反色,让它们看起来
自然 —— 否则你朋友的自拍看上去会像加了
绿脸滤镜。 */
img, video, picture, iframe, svg,
[style*="background-image"] {
filter: invert(1) hue-rotate(180deg);
}
小技巧:用 invert(0.92) 而不是完全的 invert(1),会得到比纯白→纯黑硬切更柔和的黑 —— 对眼睛更友好。hue-rotate(180deg) 会校正颜色,让橙色仍然是橙色(而不会变成蓝色)。
优点:处处可用,零分析。
缺点:阴影、渐变、微妙的灰度会显得怪异。某些 fixed 元素(粘性页头)在滚动时可能错位。
方法 2 —— 强制 prefers-color-scheme: dark
有些网站本来就有深色模式,但只对系统偏好作出反应。如果你把操作系统保持在浅色(因为你在用 Figma 做设计),却想让 GitHub 文档变深色呢?覆盖 matchMedia:
// 用 JS 强制 prefers-color-scheme: dark
const dark = window.matchMedia('(prefers-color-scheme: dark)');
Object.defineProperty(dark, 'matches', {
get: () => true,
configurable: true,
});
// 通知监听器(通过 addEventListener 挂钩的网站)
dark.dispatchEvent(new Event('change'));
它在任何使用 window.matchMedia('(prefers-color-scheme: dark)') 进行检测的网站上都有效 —— 所有现代 React/Vue 应用。它不适用于那些只在加载时检测一次的网站(你得重新加载),也不适用于没有 JS 检测的静态 CSS。
方法 3 —— 覆盖 CSS 自定义属性
基于 CSS 变量构建的网站(约 60% 的现代 Web 应用)通常有像 --bg-primary、--text-primary 这样的主题令牌。打开 DevTools,找到这些名字,覆盖它们:
/* 示例:Stripe Dashboard,那里一切都基于变量 */
:root {
--color-canvas-default: #0d1117 !important;
--color-canvas-subtle: #161b22 !important;
--color-fg-default: #c9d1d9 !important;
--color-fg-muted: #8b949e !important;
--color-border-default: #30363d !important;
}
优点:精确,不会破坏图片或阴影。
缺点:要在 DevTools 里挖 5 分钟才能找到变量名。网站必须使用 CSS 变量(在控制台用 document.documentElement.style 验证)。
方法 4 —— 为单个站点定制专属深色主题
对于那些你每天泡上好几个小时的网站 —— 花 30 分钟做一套量身定制的主题是值得的。有选择地覆盖:
/* 你那个普普通通的公司内网 */
body, .page-content, .sidebar, .top-bar {
background: #1a1a1a !important;
color: #e0e0e0 !important;
}
.card, .panel, .modal {
background: #242424 !important;
border-color: #333 !important;
}
a, a:visited { color: #58a6ff !important; }
a:hover { color: #79c0ff !important; }
input, textarea, select {
background: #1a1a1a !important;
color: #e0e0e0 !important;
border-color: #444 !important;
}
/* 表格 —— 对可读性最为关键 */
table th { background: #2d2d2d !important; color: #fff !important; }
table tr:nth-child(odd) { background: #1f1f1f !important; }
table tr:nth-child(even) { background: #1a1a1a !important; }
从主要容器(body、page)开始,往下深入到组件(卡片、模态框),最后收尾在表单元素和表格上。没有 !important 通常赢不了 —— 网站自己的样式具有更高的优先级。
常见的坑
- 内联的
background-image(例如style="background: url(...)") —— 方法 1 的滤镜不一定能捕获它。请单独加上[style*="background-image"]。 - 深色背景上的阴影看起来像光晕。把它们去掉(
box-shadow: none)或换成更亮的点缀色。 - 品牌色(logo、彩色图标)在反色后失去辨识度。加上例外:
.brand-logo { filter: invert(1) hue-rotate(180deg) !important; }。 - 表单常常硬编码了
color: black(内联)。明确覆盖它:input { color: #e0e0e0 !important; }。 - sticky / fixed 元素在方法 1 的滤镜下滚动时可能错位。通常可以接受,但知道这点总是好的。
如何在 JustZix 中接入
- 安装 JustZix(2 分钟)。
- 创建一个叫「深色模式」的文件夹。
- 规则「处处深色模式」:URL 模式
*,CSS = 方法 1。默认设为未启用 —— 需要时再打开。 - 为常用站点设规则:模式
https://myapp.com/*,CSS = 方法 3 或 4(带具体令牌 / 选择器)。始终启用。 - 用悬浮按钮一键切换整个文件夹 —— 一步切换明暗。
接下来做什么
同样的层次结构(通用 → 针对单站点的精确处理)也适用于其他规则类别 —— 参见示例和应用场景。深色模式只是其中最显而易见的案例。
免费安装 JustZix,终于掌控那些你每天盯着 8 小时的网站长什么样。
为这篇文章评分
暂无评分 — 成为第一个。