应优先使用 canvas 和 canvastext 等系统语义色而非硬编码十六进制值,配合 color-scheme: light dark 声明及 meta 标签启用原生控件自动适配,并规范 css 变量定义顺序与作用域以确保深色模式稳定生效。

直接用 @media (prefers-color-scheme: dark) 就能响应系统深色模式,但只靠它写死颜色或漏掉系统色关键字,大概率导致对比度崩坏、表单控件白底黑字、SVG图标发灰——这些不是浏览器 bug,是没桥接好系统语义。
为什么 Canvas 和 CanvasText 比 #121212 更稳
硬写十六进制色值会脱离系统上下文:比如 #121212 在 macOS 深色模式下可能比系统 Canvas 色更暗,导致滚动条、输入框边框模糊;而 Canvas 是浏览器原生语义色,随系统高对比度/深色/浅色设置自动调整亮度和饱和度。
- 所有基础背景、文字优先用
background-color: Canvas、color: CanvasText,不是“替代”,而是“兜底” - 仅在需要精确控制时才用 CSS 变量覆盖,例如
--color-bg: Canvas→@media (prefers-color-scheme: dark) { :root { --color-bg: #121212; } } - 别混用:
color: CanvasText和color: var(--text-primary)同时存在且未对齐,会导致部分元素跳变 -
Canvas等系统色不支持透明度,如需半透,得用color-mix(in srgb, CanvasText 70%, transparent)(Chrome 111+)或 fallback 到变量
@media (prefers-color-scheme: dark) 必须配合 color-scheme: light dark
光写媒体查询,<input>、<select></select>、<textarea></textarea> 这些原生控件仍会保持浅色外观——因为浏览器默认认为你“不支持”深色模式。
- 必须在
中加<meta name="color-scheme" content="light dark">,这是启用 UA 控件自动适配的开关 - 同时在
:root或html上声明color-scheme: light dark,CSS 层叠中它比媒体查询优先级低,但能触发底层渲染逻辑 - 没有
color-scheme声明时,@media (prefers-color-scheme: dark)仍生效,但表单控件、滚动条 thumb、<progress></progress>等全靠手动重写,工作量翻倍 - 旧版 Safari(12.0 及更早)完全忽略
color-scheme,只能靠媒体查询 + 变量强覆盖,但 iOS 12.1+ 已覆盖绝大多数用户
SVG 和图片在深色模式下的常见翻车点
SVG 内联图标不会自动反色,<img> 更不会——它们吃的是 fill/stroke 或 src 路径,跟 prefers-color-scheme 零关系。
- 内联 SVG:用
fill: currentColor或fill: var(--icon-fill),再通过媒体查询更新变量,别用filter: invert(1),它会让 logo 图标语义丢失、高对比度模式下不可读 - 外部 SVG:
<img src="icon.svg">无法响应主题,要么提供两套资源(icon-dark.svg/icon-light.svg),要么改用<object></object>或 inline - PNG/JPEG:深色模式下若背景是纯黑,浅色 logo 显得刺眼,建议用 SVG 替代,或加
picture+media="(prefers-color-scheme: dark)"切图 - 不要给
img批量加filter: brightness(0.8),它会降低所有图片可访问性,尤其对色觉障碍用户
变量定义顺序错一个位置,整个深色模式就失效
:root 里的变量不是“定义一次就全局生效”,它的值取决于声明顺序和是否被后续规则覆盖。很多页面深色模式不生效,根本不是媒体查询没触发,而是变量压根没被读到。
- 必须先写完整浅色默认:
:root { --color-bg: Canvas; --color-text: CanvasText; },不能只写深色分支 - 媒体查询必须写在所有默认样式之后,否则会被后面无条件的
:root覆盖(CSS 是从上到下解析,不是按媒体类型排序) - 禁止在媒体查询里定义新变量名,例如
@media (prefers-color-scheme: dark) { :root { --color-bg-dark: #121212; } }—— 浅色模式下var(--color-bg-dark)是 undefined,回退到浏览器默认 - 滚动条、selection、highlight 这些伪元素必须单独写,
scrollbar-color: ThumbColor TrackColor不继承 CSS 变量,得显式用Canvas或变量值
最易被忽略的其实是「系统色和变量的边界」:CanvasText 是系统语义,--text-primary 是你业务层抽象,两者不该混用又不桥接。一旦开始手动切换主题(data-theme),就必须让 JS 把系统色映射进变量,而不是指望浏览器自动同步。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











