直接用@media (prefers-color-scheme: dark)生效需满足三条件::root先定义浅色变量、媒体查询置于所有默认样式之后、:root或html声明color-scheme: light dark;漏一则导致深色下白底黑字或文字消失。

直接用 @media (prefers-color-scheme: dark) 就能生效,但必须满足三个硬性条件:所有 CSS 变量在 :root 中先定义浅色默认值、媒体查询块必须放在所有默认样式之后、color-scheme: light dark 要加到 :root 或 html 上——漏一个,页面在深色系统下大概率白底黑字或文字消失。
为什么写了 @media (prefers-color-scheme: dark) 却没反应
不是浏览器不支持,而是 CSS 层叠逻辑把它“吃掉”了。它本身没有更高优先级,只靠声明顺序生效。
- 深色规则写在浅色样式前面 → 后面无条件的
:root { --bg: #fff }直接覆盖掉暗色赋值 - 把媒体查询嵌套在其他查询里,比如
@media (max-width: 768px) { @media (prefers-color-scheme: dark) { ... } }→ Safari 13.3 之前整块忽略 - CSS 是 JS 动态插入的
<style>→ 部分浏览器不触发匹配,尤其首次加载时 -
:root里只写了暗色变量,没设浅色默认值 → 浅色系统下var(--bg)回退为unset,body 背景可能变透明或继承黑色
如何正确定义和覆盖 CSS 变量
变量不是全局常量,它的值取决于声明位置和是否被后续规则重写。安全做法是“先兜底、再覆盖”,且不能新增变量名。
- 第一步:在
:root顶部完整定义所有变量的浅色值,例如--text-primary: #1a1a1a、--bg-surface: #ffffff - 第二步:在所有默认样式之后,用
@media (prefers-color-scheme: dark) { :root { ... } }整体重写,变量名必须完全一致 - 第三步:显式加上
:root { color-scheme: light dark; },否则<input>、滚动条、SVG 等原生控件不会自动适配 - 别用
var(--color, #fff)这种 fallback 写法来“补救”未定义变量——#fff 不会随主题变,反而在暗色下刺眼
表单、滚动条、SVG 为什么还是亮色
它们不读取 CSS 自定义变量,也不响应 @media (prefers-color-scheme),除非你主动接管样式或通知浏览器语义意图。
立即学习“前端免费学习笔记(深入)”;
- 必须加
color-scheme: light dark到:root或html,这是让 UA 样式(如 Chrome 的输入框边框、Firefox 的 select 下拉箭头)自动切换的前提 -
<input>、<textarea>、<select>需显式重置:比如input { background: var(--bg-surface); color: var(--text-primary); } - 滚动条要单独写:
::-webkit-scrollbar-thumb { background: var(--border-default); } - SVG 的
fill和stroke默认不继承,得加fill: currentColor或直接引用变量
手动切换和系统偏好怎么共存
prefers-color-scheme 是只读信号,不能被 JS 修改,也不能覆盖用户选择。一旦加了“切换按钮”,就必须引入 data-theme 作为唯一可信状态源。
- 初始 HTML 就该带
<html data-theme="auto">,服务端可读 localStorage 或请求头预设 - CSS 里写两层:先
[data-theme="dark"] { :root { ... } },再@media (prefers-color-scheme: dark) [data-theme="auto"] { :root { ... } } - JS 切换时只改
document.documentElement.setAttribute('data-theme', 'dark'),不碰媒体查询 - 别混用
class="dark"和data-theme,容易因优先级冲突导致样式错乱
最容易被忽略的是:所有变量必须在 :root 顶层初始化,且不能依赖构建工具提前展开;color-scheme 声明不是可选,而是原生控件能否正确变色的关键开关;手动切换后若想“恢复跟随系统”,必须把 data-theme 设回 auto,而不是删掉属性——空属性和未定义属性在 CSS 选择器里行为完全不同。


















