使用 @nuxtjs/color-mode 模块配合 :root CSS 变量和 prefers-color-scheme 媒体查询,可实现系统级自动响应且 SSR 无闪屏;需避免在 mounted 中直接调用 matchMedia,应交由模块自动管理主题同步与变量注入。

直接用 @nuxtjs/color-mode 模块,配合 :root CSS 变量 + prefers-color-scheme 媒体查询兜底,就能实现系统级自动响应,且服务端渲染不闪屏。
为什么不能在 mounted 里读取 matchMedia
服务端没有 window 和 matchMedia,如果在 onMounted 或 setup 中直接调用,Nuxt 3 构建时会报错或 SSR 渲染出错误主题(比如服务端默认渲染 light,客户端才切 dark,造成闪屏)。useColorMode() 内部已做环境判断,它会在客户端才真正监听系统偏好变化。
常见错误写法:
onMounted(() => {
const media = window.matchMedia('(prefers-color-scheme: dark)')
media.addEventListener('change', ...) // ❌ SSR 报错,且首次渲染无法同步
})正确做法是完全交由 @nuxtjs/color-mode 管理,它会在 hydration 后自动读取并同步状态。
立即学习“前端免费学习笔记(深入)”;
如何让 :root 变量随系统主题实时更新
必须把主题变量定义在 :root,而不是 body 或某个 class 下——否则服务端生成的 HTML 无法提前注入对应值,客户端 JS 切换时才会生效,导致首屏样式错乱或闪动。
推荐结构:
:root {
--color-bg: #fff;
--color-text: #111;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg: #111;
--color-text: #eee;
}
}
/* 供 color-mode 覆盖的 fallback */
html.theme-dark :root {
--color-bg: #111;
--color-text: #eee;
}
html.theme-light :root {
--color-bg: #fff;
--color-text: #111;
}-
@media (prefers-color-scheme: dark)是纯 CSS 层兜底,服务端可识别,首屏即生效 -
html.theme-dark :root是 JS 主动切换时的覆盖层,由@nuxtjs/color-mode自动添加theme-darkclass 到html元素 - 两个来源互不干扰,CSS 优先级也合理(媒体查询 > 普通 class)
配置 @nuxtjs/color-mode 避免手动干预
在 nuxt.config.ts 中启用模块,并设 preference: 'system':
export default defineNuxtConfig({
modules: ['@nuxtjs/color-mode'],
colorMode: {
preference: 'system',
fallback: 'light',
hid: 'nuxt-color-mode-script',
classPrefix: '',
classSuffix: '-mode',
storageKey: 'nuxt-color-mode'
}
})-
preference: 'system'表示优先读取系统设置,不是用户手动选择的结果 -
fallback: 'light'是当系统无法判断(如旧浏览器)时的保底值,不影响 SSR 渲染逻辑 - 模块会自动注入
<script></script>在,内含最小化初始化逻辑,无需额外useColorMode()调用 - 所有页面组件中可直接用
useColorMode().value读取当前模式(仅客户端可用)
切换时避免样式抖动的关键点
即使用了 @nuxtjs/color-mode,仍可能因 CSS 写法不当导致局部重绘跳变。核心原则是:所有颜色、背景、边框都必须通过 var(--xxx) 引用,且禁止在组件内硬编码色值。
- 编辑器、代码块、表格等子区域要显式继承:
color: var(--color-text); background: var(--color-bg); - 避免对
:root批量document.documentElement.style.setProperty()—— 这会触发全量重绘,尤其在 Safari 上卡顿明显 - 动画过渡只加在轻量属性上,例如:
transition: background-color 0.2s, color 0.2s;,别加box-shadow或transform - 若需点击切换按钮触发平滑过渡,可用
document.startViewTransition(需检查支持性),但系统自动切换场景下无需手动加
最易被忽略的是:第三方组件库(如 shadcn-nuxt 或 element-plus)默认不接入你的 CSS 变量体系,必须在它们的 theme 配置里显式映射,否则按钮、输入框等仍保持原色。


















