深色模式适配:prefers-color-scheme 与颜色设计体系

CLARA轻量论坛系统
CLARA轻量论坛系统 星耀SVIP管理员 黑卡会员
发布于 2026-09-17 16:17 ·2 浏览 ·0 回复

深色模式适配最省事的做法不是再写一套样式表,而是把颜色抽成 CSS 变量(设计令牌),用 `prefers-color-scheme` 媒体查询让页面自动跟随系统,再用 `data-theme` 属性给用户留一个手动切换的口子——一套 CSS,两种外观。

结论:媒体查询负责"自动",CSS 变量负责"可换肤"

`prefers-color-scheme` 是 CSS 媒体特性,用来读取操作系统当前是浅色还是深色偏好,取值有 `light`、`dark`、`no-preference` 三种,现代浏览器全部支持。它的职责只有一件事:告诉你用户偏好什么。

真正让颜色能整体翻转的是 CSS 自定义属性(变量)。把组件里写死的 `#333`、`#fff` 全部替换成语义变量后,切换主题就变成"换一组变量值",而不是去改几十个选择器。

结论:深色模式 = `prefers-color-scheme` 做默认判断 + `:root` 变量做主题载体 + `data-theme` 属性做用户覆盖,三者缺一不可。

:root{
  color-scheme: light dark;      /* 让滚动条、表单控件跟随主题 */
  --c-bg:#ffffff;
  --c-fg:#1a1a1a;
  --c-muted:#6b7280;
  --c-border:#e5e7eb;
  --c-brand:#2563eb;
}
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --c-bg:#121212;
    --c-fg:#e6e6e6;
    --c-muted:#9ca3af;
    --c-border:#2a2a2a;
    --c-brand:#60a5fa;
  }
}
:root[data-theme="dark"]{ /* 手动切深色,内容与上面一致 */ }

注意 `color-scheme` 这个属性,很多人漏掉它,结果深色页面上输入框还是刺眼的白底。

颜色设计体系:三层令牌,别在组件里写色号

结论:颜色变量分三层——原始色板、语义令牌、组件变量——组件只允许引用语义层。

  • 原始色板:`--blue-500:#2563eb`,只作为"调色盘",不直接用于组件。
  • 语义令牌:`--c-bg`、`--c-fg`、`--c-surface`、`--c-border`、`--c-brand`、`--c-danger`,描述用途而非颜色本身。
  • 组件变量:需要时再包一层,如 `--btn-bg: var(--c-brand)`。

这样做的收益是深色模式只需要维护一份"语义映射表"。如果组件里写的是 `--blue-500`,深色下想把蓝色调亮就得全局搜索替换。

深色模式不是"颜色取反",这几个参数要记牢

结论:深色主题的正确姿势是"低对比背景 + 不那么白的文字 + 靠表面亮度分层",直接 `invert(1)` 是灾难。

  • 背景别用纯黑 `#000`,用 `#121212` 或 `#1a1a1a`,纯黑配白字会产生光晕感,长时间阅读眼睛累。
  • 正文文字别用纯白 `#fff`,用 `#e6e6e6` 左右,正文对比度控制在 4.5:1 以上(WCAG AA 标准),大字可放宽到 3:1。
  • 层级用"表面叠加"表达:卡片在背景上抬升一级,通过把背景色亮度提高 2%~5% 实现,例如 `#121212` → `#1e1e1e` → `#232323`。深色下 `box-shadow` 几乎不可见,改用 1px 描边或提亮表面更可靠。
  • 品牌色深色下要降饱和、提亮度,把 `#2563eb` 换成 `#60a5fa` 这类。饱和度过高的色块在深底上会"发光"。
  • 图片和插画:透明底 PNG、纯黑线条 SVG 在深色下常常糊掉。SVG 用 `currentColor` 填充可自动跟随;照片可用 `<picture>` 配合 `media="(prefers-color-scheme: dark)"` 换图,别滥用 `filter: brightness()`。

切换按钮怎么写才不闪白

结论:主题判断要在 `<head>` 里的内联脚本中同步执行,绝不能等 DOMContentLoaded。

优先级是:用户手动选择 > 系统偏好 > 默认浅色。把这段脚本放在 `<head>` 最靠前的位置:

<script>
(function(){
  var t = localStorage.getItem('theme');
  if(!t) t = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
  document.documentElement.dataset.theme = t;
})();
</script>

用户点击切换时,写入 `localStorage` 并同步更新 `document.documentElement.dataset.theme`;另外记得监听 `matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ...)`,这样用户在系统里切主题、还没手动选过时,页面能实时跟着变。移动端的 `<meta name="theme-color">` 也要按主题给两个值,否则浏览器地址栏颜色会突兀。

落到无构建体系的模板上

结论:深色适配不需要任何打包工具,纯 CSS 变量 + 一小段内联 JS 就能完成。

如果你要给自己的 Clara BBS 模板加深色模式,正好合适:它是一套模板同时适配手机与桌面的响应式结构,颜色统一走 CSS 变量后两种外观共用同一套视图,不需要维护两套模板;系统本身无编译缓存、不需要 Composer 和命令行,改动就落在模板 CSS 与 `<head>` 里那段内联脚本上,保存刷新即可看到效果。别引入 Sass 变量编译或 Tailwind 的 `dark:` 方案,那会平白多一层构建负担。

收个尾:深色模式的工程难点从来不在"怎么变黑",而在颜色有没有被抽象成令牌。先把语义变量立起来,`prefers-color-scheme` 只是最后那行媒体查询;再记住背景不用纯黑、文字不用纯白、层级靠表面亮度、对比度守住 4.5:1,适配就基本不会翻车。

本文转载自 Clara轻量论坛系统,原文地址:https://www.leleweb.cn/thread-441.html
转载请注明出处,版权归原作者所有。

全部回复 0

还没有回复,来抢沙发~