@media (prefers-color-scheme: dark) 必须作为顶层独立规则书写,且置于依赖样式之前;需配合 :root 变量成对重定义、color-scheme 声明及手动/系统主题逻辑隔离,否则在 safari 等浏览器中静默失效。

直接用 @media (prefers-color-scheme: dark) 就能生效,但必须写对位置、顺序和作用域,否则浏览器会静默忽略整块规则。
为什么写了 @media (prefers-color-scheme: dark) 却没反应
最常见原因是规则被提前覆盖或嵌套错误。它不是选择器,不能塞进其他媒体查询里,也不能挂在类名后面。
- 错误写法:
.theme-dark @media (prefers-color-scheme: dark) { ... }或@media (min-width: 768px) { @media (prefers-color-scheme: dark) { ... } }—— Safari 12–16.4 和部分 Android WebView 直接跳过 - 正确写法:必须是顶层独立语句,且放在所有依赖它的样式之前(比如
:root变量定义之后、组件样式之前) - 检查方法:在 DevTools 的 Styles 面板里切换
prefers-color-scheme模拟,看目标规则是否带删除线;没删线但没生效,大概率是 selector 权重不够或被!important/ 内联样式压制
:root 变量必须成对声明,不能只覆盖一部分
浏览器不会“回填”未声明的变量值,var(--bg-primary) 在暗色块里没定义,就 fallback 到 transparent 或初始值,按钮可能变透明、文字消失。
- 明色模式下先完整声明:
:root { --bg-primary: #ffffff; --text-primary: #1a1a1a; --border-base: #e0e0e0; } - 暗色块里必须重写全部相关变量:
@media (prefers-color-scheme: dark) { :root { --bg-primary: #121212; --text-primary: #e0e0e0; --border-base: #333333; } } - 别漏掉表单控件、伪元素等需要继承的变量——它们不走 class 路径,只认
:root层级
原生控件要变色,光靠媒体查询不够
color-scheme: light dark 是必需的开关,否则 <input>、<select></select>、滚动条等仍用亮色边框和白底,尤其在 Safari 下明显。
- 必须加在
html或:root上:html { color-scheme: light dark; } - 但它只是“请求”,不是“指令”:仍需手动写
input { background: var(--bg-primary); color: var(--text-primary); },否则输入框背景还是白的 - 支持范围有限:Chrome 85+、Firefox 96+、Safari 13.1+;老版本直接忽略,得靠 JS +
data-theme回退
手动切换和系统偏好混用时,优先级容易乱
用户点了“切暗色”按钮后,如果还保留 @media (prefers-color-scheme: dark) 规则,两套逻辑会打架——比如系统是浅色,但用户强制选深色,结果页面一半深一半亮。
- 推荐做法:用
html[data-theme="dark"] :root覆盖变量,且把这条规则写在媒体查询之后(确保更高优先级) - 初始化时优先读
localStorage.getItem('theme'),没值再 fallback 到matchMedia('(prefers-color-scheme: dark)').matches - 监听系统变化仅限用户未手动设置时:
matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { if (!localStorage.getItem('theme')) { /* 应用新值 */ } })
真正难的不是写对一行媒体查询,而是变量声明顺序、原生控件补全、手动/系统逻辑隔离这三处——漏掉任何一处,都会在某个设备或某个操作路径下出问题。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











