@bams-app/theme¶
📄 创建: 刘泽伟 2026-08-04
文档说明¶
本文档基于 @bams-app/theme 包整理,说明主题换肤模块的功能边界、接入方式、运行机制,以及颜色变量的命名与分层规范,供前端项目统一接入和扩展。
适用目录:/Users/wei/bams-work/work/packages/theme
模块定位¶
@bams-app/theme 将主题相关能力统一封装为独立包,目标是让业务项目以较低成本获得一致的换肤能力,同时避免颜色变量散落在各业务模块中。
模块当前提供以下能力:
- 主题模式管理:
light、dark、auto - 主题预设管理:内置经典蓝、松石绿、暮光紫、青柠绿、活力橙
- Pinia 主题 Store:统一管理模式、预设、解析后的明暗态和主题类名
- CSS 变量注入:通过
theme-*类挂载语义变量 - 跟随系统主题:
auto模式下自动监听系统明暗变化 - Ant Design Vue 主题适配:输出
theme配置对象 - DevExtreme 主题同步:切换 DOM 主题时同步
devextreme/ui/themes和devextreme/viz/themes - 局部主题变量能力:组件可按页面级、容器级单独挂载局部变量
- 现成的切换 UI:
ThemePanel - 环境开关控制:通过
VUE_APP_ENABLE_THEME控制是否启用
导出能力¶
模块统一从 src/index.js 导出:
- 常量:
DEFAULT_THEME_MODE、DEFAULT_THEME_PRESET、THEME_MODES、THEME_PRESETS - 运行时:
resolveThemeMode()、applyTheme()、setupTheme()、cleanupTheme() - Store:
createThemeStore()、useThemeStore()、useThemeState() - UI 桥接:
createAntdThemeConfig()、useAntdThemeConfig() - 工具方法:颜色处理、系统主题识别、开关解析等
- 局部变量 Hook:
useLocalThemeVars() - UI 组件:
ThemePanel
启用方式¶
主题功能默认关闭,只有显式配置环境变量时才开启。
环境变量:VUE_APP_ENABLE_THEME
启用值:
1trueonyes
未设置或其他值时,模块退回默认主题:
- 模式:
light - 预设:
material.blue
示例:
标准接入方式¶
1. 安装 Pinia 和持久化插件¶
主题 Store 使用 Pinia,且默认只持久化以下字段:
modepreset
默认本地存储 key 为:[bams-app]-theme
2. 在应用入口初始化¶
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
import { setupTheme, useThemeStore } from "@bams-app/theme";
import "@bams-app/theme/src/theme.less";
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
const themeStore = useThemeStore();
themeStore.init();
setupTheme();
推荐顺序:
- 注册 Pinia
- 挂载持久化插件
- 引入
theme.less - 执行
themeStore.init(),校验持久化值是否合法 - 执行
setupTheme(),同步 DOM 类名、系统主题监听和第三方主题
运行机制¶
1. Store 维护主题状态¶
Store 中维护以下核心状态:
mode:用户选择的模式,值为light | dark | autopreset:当前主题预设,如material.blueresolvedMode:真正生效的模式,仅为light | darkmodeClass:如theme-lightpresetClass:如theme-material-blue
2. setupTheme 负责主题同步¶
setupTheme() 会完成以下事情:
- 初次执行
applyTheme(),把主题类挂到根节点 - 监听 Store 状态变化,自动同步主题
- 在
auto模式下监听prefers-color-scheme - 同步 DevExtreme 图表和组件主题
3. applyTheme 的核心逻辑¶
applyTheme() 不是直接写 CSS 变量,而是通过组合类名生效:
- 明暗模式类:
theme-light/theme-dark - 主题预设类:
theme-material-blue/theme-material-teal等
最终会把类挂到根元素,并保留兼容属性:
data-themedata-color-mode
这样做的好处是:
- 变量来源集中在样式层维护
- DOM 上的主题状态明确可见
- 便于组件、页面、第三方 UI 一起消费
主题切换面板¶
模块内置 ThemePanel 组件,适合直接挂到设置抽屉、个人偏好页等场景。
支持能力:
- 切换主题模式:浅色 / 深色 / 跟随系统
- 切换主题预设:5 套内置品牌色
- 面板只在主题功能开启时显示
示例:
<template>
<ThemePanel />
</template>
<script setup>
import { ThemePanel } from "@bams-app/theme";
</script>
状态读取与主题修改¶
只读场景¶
业务组件如果只消费主题状态,推荐使用 useThemeState():
<script setup>
import { useThemeState } from "@bams-app/theme";
const { mode, preset, resolvedMode, modeClass, presetClass } = useThemeState();
</script>
读写场景¶
如果组件既要展示当前状态,又要切换主题,推荐组合使用:
useThemeState():读取响应式状态useThemeStore():调用 action
<script setup>
import { useThemeState, useThemeStore } from "@bams-app/theme";
const themeStore = useThemeStore();
const { preset, resolvedMode } = useThemeState();
const switchDark = () => {
themeStore.setMode("dark");
};
</script>
与 Ant Design Vue 集成¶
如果项目使用 Ant Design Vue,推荐在根组件中把主题 Store 映射为 antd 的 theme 配置。
<script setup>
import { theme as antdTheme } from "ant-design-vue";
import { useAntdThemeConfig, useThemeStore } from "@bams-app/theme";
const themeStore = useThemeStore();
const antdThemeConfig = useAntdThemeConfig(themeStore, antdTheme);
</script>
模块当前映射了两个关键 token:
colorPrimarycolorInfo
算法切换规则:
- 浅色模式:
defaultAlgorithm - 深色模式:
darkAlgorithm
局部主题变量¶
如果某个页面或容器想使用自己的主题变量,而不依赖全局根节点注入,可以用 useLocalThemeVars() 或手动计算 :style。
适用场景:
- 单个页面想局部定制视觉
- 某个容器要与全局主题保持一致,但又要补充私有变量
- 弹出层希望继承当前容器变量,而不是
document.body
使用原则:
- 局部变量仍然建议沿用
--bams-*语义命名 - 模式差异放在
themeModeVars - 品牌差异放在
themePresetVars - 弹出层组件通过
getPopupContainer绑定到当前容器
内置主题预设¶
当前预设定义在 THEME_PRESETS 中:
| 名称 | value | 主色 |
|---|---|---|
| 经典蓝 | material.blue |
#0d6cbd |
| 松石绿 | material.teal |
#13c2c2 |
| 暮光紫 | material.purple |
#722ed1 |
| 青柠绿 | material.lime |
#aeea00 |
| 活力橙 | material.orange |
#ff6d00 |
新增预设时,需要同时维护:
constants.js中的THEME_PRESETStheme-presets.less中对应的.theme-*类
颜色变量架构规范¶
主题变量采用分层架构,避免把品牌色、模式色、状态色混在一起。
1. 架构分层¶
样式入口顺序如下:
architecture.lessroot-tokens.lesstheme-presets.lesstheme-light.lesstheme-dark.lessglobal.less
各层职责:
:root¶
全局默认语义变量,提供兜底值,保证即使主题功能关闭,页面仍有可用颜色系统。
.theme-material-*¶
主题预设层,只覆盖品牌相关变量,不负责背景、文本、边框等中性色。
.theme-light / .theme-dark¶
模式层,负责页面背景、文本层级、边框、阴影、浮层、交互态等中性色系统。
组件样式层¶
组件内部只消费语义变量,不直接写死颜色值。
2. 命名规范¶
统一使用 --bams-* 前缀,按语义而不是按业务组件命名。
推荐命名族:
- 品牌主色:
--bams-primary-* - 链接/信息:
--bams-link-*、--bams-info-* - 语义状态:
--bams-success-*、--bams-warning-*、--bams-error-* - 文本层级:
--bams-text-* - 背景层级:
--bams-*-bg、--bams-surface-* - 边框层级:
--bams-border-* - 阴影/焦点:
--bams-shadow-*、--bams-focus-*
后缀含义:
color:实体色值,适合文本、图标、描边bg:背景色soft:弱化填充态hover/active/selected/disabled:交互态text:文本使用色border:边框使用色outline:外发光、聚焦描边等
3. 变量职责规范¶
品牌色变量¶
品牌预设层负责这类变量,例如:
--bams-primary--bams-primary-hover--bams-primary-active--bams-primary-border--bams-primary-soft--bams-primary-bg--bams-primary-outline
这类变量应只表达品牌色及其衍生态,不应混入明暗模式判断。
模式层变量¶
明暗模式层负责中性色和结构层级,例如:
- 页面和容器背景:
--bams-page-bg、--bams-surface-bg - 文本层级:
--bams-text-color、--bams-text-secondary - 边框层级:
--bams-border-color、--bams-border-strong - 浮层和遮罩:
--bams-overlay-bg、--bams-mask-bg - 组件交互:
--bams-hover-bg、--bams-selected-bg
这类变量应只表达浅色和深色差异,不应写死具体品牌色方案。
状态色变量¶
成功、警告、错误等状态色定义在根变量层,通常跨预设共用:
--bams-success-*--bams-warning-*--bams-error-*
除非确有品牌策略要求,不建议把状态色跟随主题预设一起变化。
推荐实践¶
- 组件尽量消费语义变量,不直接写
#xxxxxx - 品牌色变化放到预设层,不要散落在业务组件里
- 明暗差异放到
theme-light.less/theme-dark.less - 业务组件新增颜色时,优先补语义变量,再在组件里使用
- 新增主题预设时,同时补充常量定义和
.theme-*样式类 - 主题关闭时也要保证默认主题可用,因此
:root必须有兜底值 - 对接第三方组件库时,优先由主题 Store 派生其配置对象,不要在页面里重复映射
不推荐做法¶
- 在业务组件里直接写死品牌色
- 在
theme-light.less中定义某个预设专属颜色 - 在
theme-presets.less中覆盖大面积背景、边框、文本层级 - 让组件直接依赖
material.blue这类预设名判断逻辑 - 新增变量时使用无语义命名,如
--blue-1、--card-color-2
维护建议¶
如果后续继续扩展主题体系,建议遵循以下顺序:
- 先明确变量属于品牌层、模式层还是状态层
- 再决定应该加到
root-tokens.less、theme-presets.less、theme-light.less或theme-dark.less - 最后再让业务组件消费这个变量
这样可以保证:
- 变量职责清晰
- 新主题扩展成本低
- 主题切换时不容易出现视觉串色
- 第三方组件与业务组件的换肤规则保持一致
总结¶
@bams-app/theme 的核心思想不是“切换几组颜色”,而是通过 Store + DOM 类名 + 分层语义变量,建立一个可维护、可扩展、可复用的前端主题系统。
对于业务接入方,最关键的原则是:
- 入口统一初始化
- 组件统一消费语义变量
- 品牌色、模式色、状态色分层维护
- 扩展预设时同时维护常量与样式映射
按这个规范执行后,模块可以稳定支撑全局换肤、局部换肤,以及 Ant Design Vue / DevExtreme 等第三方库的主题同步。