ssr 中直接操作 document 会失败,next.js 必须用 next-themes;避免 fouc 需服务端预设 data-theme、顶层注入、正确书写 :root 变量、组件级变量需服务端注入值,并防范 css 变量名冲突。

服务端渲染(SSR)中直接用 document.documentElement.style.setProperty() 设置 CSS 变量必然失败——因为 Node.js 环境没有 DOM,document 是 undefined。
Next.js 里必须用 next-themes 而不是手动读 localStorage
你在 useEffect 里写 localStorage.getItem('theme') 再调 document.documentElement.setAttribute('data-theme', ...),页面首屏一定会闪:白底 → 黑底 → 白底 → 黑底。这不是 CSS 变量的问题,是执行时机错位。
-
next-themes在服务端就根据defaultTheme和用户prefers-color-scheme头部“猜”出主题,并提前注入 - 它把
ThemesProvider强制放在app/layout.tsx顶层,确保 SSR 输出的 HTML 已带正确data-theme - 别自己封装
useThemeHook,哪怕加了if (typeof window !== 'undefined')也救不了 FOUC
:root 中的变量定义不能套在 @media 或嵌套选择器里
Next.js 的 SSR 会提取 app/layout.tsx 中 import './globals.css' 的内容并内联到 ,但只提取顶层规则。媒体查询里的 :root 声明不会被提取,客户端 JS 激活前变量为空。
- ✅ 正确写法:
:root { --bg: #fff; }+[data-theme="dark"] { --bg: #111; } - ❌ 错误写法:
@media (prefers-color-scheme: dark) { :root { --bg: #111; } } - ❌ 错误写法:
.theme-wrapper :root { --bg: #111; }(选择器不匹配根节点)
组件级 CSS 变量在 SSR 中要靠服务端注入值,不能依赖客户端 JS
如果你在某个卡片组件里用 .card { --card-bg: #f9fafb; },然后子元素用 background: var(--card-bg),SSR 渲染时这个变量值必须已存在——否则子元素拿到的是 var(--card-bg) 的未定义状态,回退值又没设,就可能透明或崩样式。
- 服务端需通过
renderToStaticMarkup或自定义getServerSideProps注入style="--card-bg: #121212"到组件根元素上 - 避免在组件内部
useEffect里调el.style.setProperty(),这会让 SSR 输出和客户端不一致 - 检查 DevTools 的 “Computed” 面板,确认变量是否真被继承下来;用
getComputedStyle(el).getPropertyValue('--card-bg')手动验证
最易被忽略的一点:CSS 变量名冲突。Tailwind 的 --tw- 前缀、Ant Design 的 --ant-、你自己写的 --bg,PurgeCSS 可能直接删掉未显式使用的变量——务必加统一命名空间,比如 --theme-bg,并在 tailwind.config.js 的 safelist 里声明。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











