小程序不支持动态修改 :root CSS 变量,须用 class 切换主题;需在 App.vue 中读取本地主题、广播变更、操作根节点 class,并同步调用 tabBar 和导航栏原生 API 更新颜色。

小程序端不支持 :root CSS 变量动态修改,必须用 class 切换
微信小程序的 Webview 渲染层不支持运行时修改 :root 的 CSS 变量值,document.documentElement.style.setProperty 在小程序里完全无效。强行调用不会报错,但样式毫无变化——这是最常被踩的坑。
正确做法是提前在 App.vue 或全局样式表中定义好主题 class(如 .theme-light、.theme-dark),每个 class 内部重写所有关键 CSS 变量或直接覆盖属性:
:root {
--primary-color: #007aff;
}
.theme-dark {
--primary-color: #1e293b;
}
/* 小程序实际生效的是下面这种写法 */
.theme-dark page,
.theme-dark view,
.theme-dark .uni-btn {
color: #f1f5f9;
background-color: #1e293b;
}切换时只需给根节点(如 #app 或 <page>)切换 class 名,无需操作样式表。
如何在 App.vue 中安全注入并持久化主题 class
小程序启动时需读取本地存储的主题标识,并同步到根容器;同时监听前台切换防止后台切回后状态丢失。
- 在
App.vue的onLaunch和onShow中都执行:uni.getStorageSync('theme')获取当前主题 - 用
uni.$emit('themeChange', theme)广播变更,避免依赖watch(小程序中 watch 响应不及时且易漏触发) - 通过
uni.createSelectorQuery().select('#app').exec()获取根节点,调用el.classList.add('theme-dark')(需搭配classList.remove清旧 class) - 务必在
onHide或页面onUnload时清理事件监听,否则多次进入会重复绑定
原生组件(如 tabbar、navigationbar)必须单独适配
小程序的 tabBar 和导航栏颜色不随 CSS class 变化,必须显式调用 API 同步更新:
- 切换主题后立即调用
uni.setTabBarStyle({ backgroundColor: '#1e293b', color: '#f1f5f9' }) - 调用
uni.setNavigationBarColor({ frontColor: '#ffffff', backgroundColor: '#1e293b' }) - 注意:这些 API 在 H5/App 端无效,需用条件编译包裹:
#ifdef MP-WEIXIN - 图标资源若为 PNG,无法改色;必须换成 SVG 并用
fill="var(--primary-color)"绑定变量
不要在组件内用 v-if 判断主题,改用 CSS 变量 + class 绑定
写 v-if="theme === 'dark'" 不仅难维护,还会让组件失去复用性;更严重的是,小程序中 data 更新不一定会触发原生组件重绘。
推荐方式:
- 所有颜色值统一用
var(--text-primary)、var(--bg-surface)等 CSS 变量声明 - 组件根元素加
:class="['component-root', `theme-${theme}`]",由 class 控制变量值 - 避免在
:style中硬编码颜色,尤其不要对uni-button、uni-input这类原生封装组件做运行时 style 覆盖
真正容易被忽略的点是:主题切换不是“改一个变量就完事”,而是 JS 状态、CSS class、原生 API 三者必须严格同步,缺一不可。任何一环滞后,用户就会看到短暂的样式错乱或原生控件颜色残留。


















