根本原因是css层叠顺序错误:深色规则若写在浅色:root定义之前,会被后续默认值覆盖;必须先完整声明所有浅色变量,再用@media (prefers-color-scheme: dark) { :root { ... } }覆盖,且不可漏定义、不可嵌套、需加fallback和color-scheme声明。

为什么写了 @media (prefers-color-scheme: dark) 却没切换
不是媒体查询没触发,是 CSS 层叠顺序搞错了。浏览器按声明顺序执行,@media 块本身不自动提升优先级。如果深色规则写在浅色 :root 定义之前,后面无条件的默认值会把它盖掉。
- 必须先定义完整浅色
:root变量块(所有变量都要有初始值) - 紧接着紧跟
@media (prefers-color-scheme: dark) { :root { ... } }覆盖块 - 漏定义某个变量(比如只写了
--bg没写--text),深色块里再赋值也无效——变量作用域从首次声明起才存在 - 别用嵌套媒体查询:
@media (max-width: 768px) { @media (prefers-color-scheme: dark) { ... } },旧版 Safari(≤13.3)直接不匹配
:root 里变量怎么初始化才不翻车
所有主题变量必须在 :root 中明确定义一次,默认值以浅色为基准;深色块里只改值,不新增变量名。命名带语义前缀,避免冲突和歧义。
- 正确写法:
:root { --color-bg-surface: #fff; --color-text-primary: #333; } - 深色覆盖:
@media (prefers-color-scheme: dark) { :root { --color-bg-surface: #121212; --color-text-primary: #e0e0e0; } } - 必须加 fallback:
background-color: var(--color-bg-surface, #fff);,否则变量未定义时可能继承透明或崩溃 - 漏掉
color-scheme: light dark;声明,原生控件(input、滚动条、select)不会自动适配
表单、SVG、滚动条这些“隐形元素”怎么一起切
它们不读取 CSS 变量,也不响应 color 或 background 的间接变化,必须显式重置。
-
svg的fill和stroke必须写成fill: var(--icon-primary);,不能靠currentColor或继承 - 滚动条需单独声明:
::-webkit-scrollbar-track { background: var(--color-bg-surface); } -
input、textarea、button的border-color、background、color全部手动替换为var() - 第三方组件(如 Ant Design)若未开启 CSS 变量支持,其内部 class 不会响应你的变量,得查文档确认是否启用
css-var模式
JS 动态干预时最容易踩的坑
系统主题是被动响应,但用户手动切换主题时,JS 往往要同步状态。这里几个点一错就全乱:
- 不要只改
dataset.theme却忘了localStorage.setItem('ui-theme', 'dark'),刷新后变回默认 - 页面加载前就得设置:
<script>document.documentElement.dataset.theme = localStorage.getItem('ui-theme') || 'light';</script>,否则必然 FOUC - 监听系统变化要用
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ...),别只靠页面加载时判断一次 - 如果同时支持手动切换 + 系统跟随,注意优先级:用户手动选择应覆盖系统偏好,且需同步写回
localStorage
最易被忽略的是:变量名大小写敏感、双短横线漏写(--primary-color 写成 primary-color 就静默失效),以及开发者工具里看到 var(--x) 没展开,说明根本没定义或拼写错——这时候查 :root 声明比调 JS 更快。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











