深色模式需显式适配所有关键元素和伪类:h1–h6、.card、button等必须在@media(prefers-color-scheme:dark)中单独声明color/background-color;:disabled、::selection须手动补全;第三方组件应禁用自动检测,统一用data-theme控制;变量必须带fallback且媒体查询须置于CSS顶层。

直接用 @media (prefers-color-scheme: dark) 写样式,却只改了 body 背景,其他元素(比如 h1、.card、button)颜色还是浅色——这不是漏写,是覆盖逻辑失效。
选择器范围太窄,导致大量元素沿用默认样式
媒体查询里只写了 body { background: #121212; },但 h1 没声明 color,它就继续用浏览器默认的黑色;.card 没定义 background-color,就继承父级或 fallback 到白色。深色模式不是“整体变暗”,而是每个可渲染属性都要显式适配。
- 必须覆盖所有关键语义元素:
h1–h6、p、a、code、pre、blockquote - 组件类不能靠“继承”:
.card、.btn、.tag这些必须单独在媒体查询中重写color、background-color、border-color - 避免“全站统一背景”思维:深色模式下
pre可能需要灰底+白字,blockquote却要蓝左边框+浅灰背景,不能只靠一个--bg变量撑全场
:disabled、::selection 等伪类被完全忽略
:disabled 在深色背景下常“隐形”,因为它的样式仍用浅色模式下的 #ccc 边框和文字;::selection 不声明时,选中文本直接变黑字叠在深色背景上——这两个伪类不会自动响应 prefers-color-scheme,必须手动补全。
-
:disabled规则必须和普通状态规则并列写,且用变量驱动:input:disabled { color: var(--text-disabled); border-color: var(--border-disabled); } -
::selection和::-moz-selection必须同时声明,且放在 CSS 最顶部,不能在 scoped 块或组件样式内;值禁用 alpha 通道,例如用#4a5568而非#4a556880 -
button、input默认user-select: none,所以button::selection永远不生效;如需高亮,得先加user-select: text
第三方组件样式与你的媒体查询冲突
Ant Design、MUI、Element Plus 这些库通常自己管理主题,它们注入的 class 或修改的 :root 变量会和你的 @media 规则打架。常见表现是:系统切深色,el-button 还是浅灰边框;或者你手动切主题后,MuiInputBase 的 placeholder 颜色错乱。
立即学习“前端免费学习笔记(深入)”;
- 禁用库的自动检测(如 MUI 的
useColorScheme、Element Plus 的darkprop),改由你用data-theme="dark"统一控制 - 不要试图用高优先级选择器覆盖库的深层结构(如
.el-input.is-disabled .el-input__inner),而是通过主题变量注入(SCSS 变量重定义或ConfigProvider配置) - 原生控件(
select、textarea)必须加color-scheme: light dark,否则即使系统是深色,下拉箭头、滚动条、光标仍为亮色
变量 fallback 缺失或作用域混乱
var(--text) 在深色媒体查询里重定义了,但某个组件里写了 color: var(--text); 却没 fallback,JS 未加载或旧浏览器下就变成透明;更隐蔽的是,嵌套组件自己定义了同名 --text,结果媒体查询里的值根本进不去。
- 每个
var()必须带 fallback:color: #333; color: var(--text, #333); -
:root中只放基础变量(--text-primary、--bg-base),组件内局部变量(如.card { --card-bg: var(--bg-base); })要在媒体查询里同步重设 - 禁止在媒体查询里定义新变量名(如
--text-dark-only),浅色环境下读不到,var(--text-dark-only)直接 fallback 到浏览器默认
最易被忽略的点:媒体查询必须写在 CSS 文件顶层,不能嵌套在 @media (max-width: 768px) 里面;Safari 对嵌套媒体查询直接跳过,而 DevTools 里还显示“已匹配”,实际根本不生效。


















