必须显式配置 darkmode: 'class',否则 dark: 类在编译时被彻底剔除;正确写法是 module.exports = { darkmode: 'class', content: ['./src/*/.{js,jsx,ts,tsx}'] },且需手动为每个颜色类添加 dark: 变体并内联初始化脚本。

必须显式配置 darkMode: 'class',否则所有 dark: 类在编译时被彻底剔除——不是 JS 没执行,是压根没生成对应 CSS 规则。
tailwind.config.js 里 darkMode 必须写对位置和值
常见错误是把 darkMode: 'class' 写进 theme.extend 或 plugins,或者写成 true、'dark'、空字符串甚至漏掉这个字段。Tailwind v3+ 不再默认启用 dark:,不写就等于关掉。
正确写法只有一种:
module.exports = {
darkMode: 'class',
content: ['./src/**/*.{js,jsx,ts,tsx}'],
// 其他配置
}
-
darkMode: 'media'→ 完全响应系统偏好,document.documentElement.classList.toggle('dark')无效 -
darkMode: 'class'→ 是唯一支持按钮切换 +localStorage持久化的方案 - SSR 框架(如 Next.js)中若漏配,首屏 hydration 会失败,出现样式错乱
切换必须操作 document.documentElement,不能碰 body
Tailwind 只检查 元素是否存在 dark 类。加在 或任意 <div> 上,<code>dark:bg-gray-800 永远不会生效。
- 错误写法:
document.body.classList.toggle('dark')—— SSR 下会触发水合 mismatch - 正确写法始终是:
document.documentElement.classList.toggle('dark') - 切换时记得同步写入:
localStorage.setItem('theme', isDark ? 'dark' : 'light')
初始化脚本必须内联在 ,且早于 CSS 加载
“闪白”不是 bug,是执行时机问题:浏览器先渲染无 dark 类的浅色样式,JS 后加类才触发深色,中间有可见延迟。
解决方式只有一种:脚本不能挂 DOMContentLoaded 或外部文件,必须写成 IIFE 并内联在 中,且包 try/catch 防 SSR 报错:
(() => {
try {
const saved = localStorage.getItem('theme')
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
const shouldDark = saved === 'dark' || (saved !== 'light' && prefersDark)
document.documentElement.classList.toggle('dark', shouldDark)
} catch {}
})()
- 漏掉内联,用户会看到 100ms~300ms 的浅色闪屏
- 不包
try/catch,服务端渲染环境直接报ReferenceError: window is not defined - 不读
localStorage、不监听storage事件,多标签页切换主题后当前页毫无反应
每处颜色都得手动补上 dark: 变体,没有自动继承
dark: 不是全局主题开关,它只对加了该前缀的工具类生效。写了 text-gray-700 却没写 dark:text-gray-300,深色下文字可能糊成一片。
- 别混用
dark:bg-gray-900和bg-white在同一元素上——CSS 特异性竞争难预测 - 推荐写法:
bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 - 避免
!important,它会破坏dark:的条件性,导致暗色模式失效 - 自定义颜色时,务必确认
content配置包含所有用到dark:的模板文件,否则 JIT 模式下类名被剔除
最易被忽略的是:配完 darkMode: 'class' 只是开了门,真正让暗黑模式可用,还得逐个组件补全 dark: 变体——这不是可选优化,是强制前提。











