prefers-color-scheme媒体查询必须顶层独立书写,不能嵌套在选择器或其它规则内;正确写法仅为@media (prefers-color-scheme: dark) { ... },且需置于CSS靠前位置以确保变量定义优先级。

prefers-color-scheme媒体查询必须顶层独立写
它不是选择器,不能嵌套在其他规则里。写成 .dark-mode @media (prefers-color-scheme: dark) { ... } 或 body@media(...) 都无效,浏览器直接忽略整块规则。
正确写法只能是独立的 @media (prefers-color-scheme: dark) { ... } 块,且必须放在 CSS 文件靠前位置(早于任何依赖它的变量定义)。
- 匹配系统级设置:macOS、Windows 10+/11、iOS 13+、Android 10+ 用户开启系统暗黑模式时自动生效
- 不响应运行时切换:Safari 旧版本(≤16.4)和部分 Android WebView 中,用户中途改系统主题,页面不会重绘,必须刷新
- 它本身不触发 JS,也不写入
localStorage,纯声明式、零 JS 开销
data-theme属性配合:root变量切换要分层隔离
用户手动点击切换时,html[data-theme="dark"] :root 是唯一安全写法。如果只写 html[data-theme="dark"] 然后在里面设变量,那些变量无法被表单控件、伪元素等继承——因为它们不在这条选择器路径下。
常见错误是漏掉默认 :root 声明。比如只在暗色块里定义 --bg-primary,但明亮模式下没声明,首次渲染时所有 var(--bg-primary) 都 fallback 到 transparent 或初始值,按钮变透明、文字消失。
立即学习“前端免费学习笔记(深入)”;
- 所有主题变量必须在顶层
:root中完整声明一次(哪怕值相同) -
html[data-theme="dark"] :root和@media (prefers-color-scheme: dark)不要混用同一组选择器,否则 specificity 冲突难调试 - 避免用
document.documentElement.style.setProperty()动态改变量——它绕过 CSS 层叠逻辑,导致 transition 失效、伪类丢失、本地存储不同步
color-scheme属性只影响原生控件,不能替代样式声明
color-scheme: dark 是个“请求”,不是“指令”。它告诉浏览器:“这个区域允许你用暗色 UI 渲染原生控件”,但不会自动改 <input> 背景、边框或文字色。
必须配合显式样式才能真正生效。而且它只对 <html> 或 :root 设置才有效,写在 body 或组件上无效。
- 支持范围有限:Chrome 85+、Firefox 96+、Safari 13.1+;老版本直接忽略
- 生效控件包括:
<input type="range">、<select>、滚动条、复选框、单选按钮等,但<textarea>和普通<input>仍需手动设background/color - 即使写了
color-scheme: dark,也得同步写input { background: #1e1e2f; color: #e2e8f0; },否则 Safari 下输入框还是亮色
伪类:not()用于批量重置样式时容易破坏可访问性
有人用 html *:not(a, code *) { color: #e2e8f0 !important; } 这种写法“一键变暗”,看似省事,实则危险。
它会强制覆盖所有元素颜色,包括 <abbr> 的 title 提示、<mark> 高亮、<ins>/<del> 编辑标记,甚至屏幕阅读器依赖的语义色对比度。
- !important 会阻断用户自定义样式、浏览器高对比度模式、扩展插件干预
- 链接
a单独设色后,a:visited、a:focus、a:hover往往被遗漏,键盘导航体验崩坏 - 更稳妥的做法是逐层定义:先设
body和section主容器,再用组件级类名精细控制,留出语义例外
prefers-color-scheme)和用户手动切换(data-theme)两条路径必须物理隔离,不能靠 JS 合并状态,也不能靠 CSS 权重博弈——否则首次加载闪屏、持久化失效、控件样式错乱会同时出现。


















