data-theme 必须设在 document.documentElement 上,否则 fixed 元素、伪元素、SVG 填色等无法响应主题;正确做法是在 <head> 内联脚本中立即设置 html[data-theme] 并同步 <meta name="color-scheme">,CSS 中使用 html[data-theme="dark"] 定义变量并为所有属性提供回退值和过渡声明。

data-theme 必须设在 document.documentElement 上
设在 body 或某个容器上,fixed 元素、伪元素(如 ::before)、SVG 的 fill 等根本不会响应主题切换——因为 CSS 变量作用域依赖于 :root,而 :root 永远指向 document.documentElement(即 html 元素)。常见错误是写 document.body.setAttribute('data-theme', 'dark'),表面样式变了,但检查 DOM 会发现 html 标签上压根没这个属性,[data-theme="dark"] 选择器根本没匹配上。
HTML 模板中必须提前写死 html[data-theme] 属性
服务端渲染或静态 HTML 模板里,不能靠 JS 延后注入 data-theme;否则首屏会闪动(FOUC),且 meta[name="color-scheme"] 同步失败。正确做法是在 <head> 里放内联脚本,立即读取 localStorage 或 prefers-color-scheme,并同步设置:
const saved = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', saved);
document.querySelector('meta[name="color-scheme"]').setAttribute('content', saved === 'dark' ? 'dark' : 'light');
注意:<meta name="color-scheme"> 必须存在且可被查到,否则浏览器无法协调系统级颜色适配。
CSS 中必须用 html[data-theme="dark"] 而非 .dark 或 :root .dark
用 class 切换(比如 body.className = 'dark')极易和 Tailwind、BEM、第三方 UI 库冲突;data-theme 是语义化属性,天然隔离,且模板直出时可写死:<html data-theme="light">。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
立即学习“前端免费学习笔记(深入)”;
-
html[data-theme="dark"] { --bg: #111; }✅ 正确,权重足够、作用域准确 -
:root .dark { --bg: #111; }❌ 错误,这是两个独立选择器,变量不会生效 -
[data-theme="dark"] { --bg: #111; }⚠️ 危险,漏掉html前缀可能导致权重不足,子元素样式覆盖它
所有样式属性都得显式调用 var(--xxx) 并提供回退值
只定义变量不使用,等于白干。比如写了 --border-color: #ccc;,但 border-color 属性没写 var(--border-color, #ccc),那切换主题时边框色根本不变。更关键的是:漏掉回退值(逗号后的默认色)会导致变量未定义时显示浏览器默认值——深色模式下黑字配黑底就是这么来的。
过渡也得提前声明:transition: background-color 0.3s, color 0.3s; 不能等 JS 改完再加 class 触发,浏览器只对已声明的属性做平滑动画。
复杂点在于 SVG 和伪元素——它们不继承父元素的 CSS 变量,必须直接写在 html[data-theme] 下或用 fill: var(--icon-fill) 显式指定,否则主题一换就“消失”了。


















