深色模式切换必须在 :root 中定义 css 变量并用 .dark 类控制值,结合系统偏好与 localstorage 管理主题状态,禁用 @media 直接赋值,组件需显式声明所有颜色变量。

深色模式切换必须用 :root 声明 CSS 变量
直接在 :root 里定义变量,才能被全局继承;如果写在某个 class 或元素选择器里,作用域受限,切换时无法批量生效。变量名建议统一加前缀(比如 --color-bg、--color-text),避免和第三方库冲突。
常见错误是把变量写在 body 或 .dark 类里——这样会导致未匹配到的子元素拿不到值,或者需要反复重写继承链。
-
:root中定义默认(浅色)值::root { --color-bg: #ffffff; --color-text: #333333; } - 用媒体查询或 class 控制深色值:
.dark { --color-bg: #1e1e1e; --color-text: #e0e0e0; } - 确保 HTML 根节点能应用
.dark类(比如通过 JS 切换document.documentElement.classList.toggle('dark'))
JS 切换时优先读取系统偏好,再 fallback 到 localStorage
用户首次访问时,别强行设为深色或浅色——应先检查 window.matchMedia('(prefers-color-scheme: dark)'),再读 localStorage.getItem('theme') 覆盖它。否则会违背用户系统设置,尤其在 macOS/iOS 上容易引发投诉。
- 设置主题的函数要同时操作 class 和 storage:
function setTheme(theme) { document.documentElement.classList.toggle('dark', theme === 'dark'); localStorage.setItem('theme', theme); } - 初始化时顺序不能错:先取系统偏好,再看本地存储,最后才设值
- 监听系统变化:
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', ...),但仅当 localStorage 为空时才响应它
避免在 CSS 中用 @media (prefers-color-scheme: dark) 替代变量切换
单纯靠媒体查询只能做一次性适配,无法支持用户手动切换。而且它和 .dark class 共存时容易覆盖混乱——比如媒体查询设了 --color-bg,又在 .dark 里重设,最终以层叠顺序为准,调试困难。
PigX UI Pro 前端开发指南 - Vue 3 + TypeScript + Element Plus。当用户提到 PigX UI、PigX 前端、lgb-mgui 项目、Vue 3 企业级后台开发、Element Plus 后台开发时使用此技能。
真正需要的是“变量驱动 + class 控制”的组合:媒体查询只用于初始化,所有样式逻辑收口到变量和 class 上。
- 删掉所有直接写在
@media块里的颜色声明,改用变量 - 不要在
@media里给:root赋新值——这会让 JS 切换失效 - 若需兼容不支持 CSS 变量的老浏览器(IE),得另配一套 class-based 方案,变量只是增强手段
组件级深色适配要警惕 inherit 和透明色陷阱
很多组件用 background: transparent 或 color: inherit,看似省事,实则在深色模式下可能完全不可见——比如一个按钮背景透明、文字 inherit,切到深色后文字变成深灰 on 深灰背景。
变量不是万能胶,每个用到颜色的地方都得显式声明,哪怕只是复用同一变量。
- 禁止依赖父级 color/ background 的隐式继承,全部显式写
color: var(--color-text) - 半透明色(如
rgba(0,0,0,0.1))在深色背景下会变浑浊,改用变量控制 alpha 或直接换色值 - border、shadow、placeholder 等次要颜色也得单独定义变量,漏掉一个就可能破局
setTheme,也别在 scroll 或 input 事件中直接改 class。前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










