9.8 KiB
9.8 KiB
name, overview, design, todos
| name | overview | design | todos | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 主题色切换与深浅色模式 | 为 fms-vue 增加主题色切换(预设色板点选主色)与深/浅色切换功能,采用纯手写 + CSS 变量驱动方案,参考 Soybean Admin 与 Vue Vben Admin 的 design tokens 设计,主题状态用 pinia 持久化。 |
|
|
产品概述
为 fms-vue 增加一套轻量主题系统,支持「主色切换」与「深/浅色切换」。方案参考 Soybean Admin 与 Vue Vben Admin 的 token 化设计:CSS 变量作为唯一运行时真相,JS 仅负责集中配置与动态写变量。保持纯手写组件、不引入任何 UI 组件库。
核心功能
- 预设主色切换:提供一组精选预设色板,点击色块一键切换品牌主色(primary),success / warning / destructive 保持固定
- 深/浅色切换:手动切换明暗模式,状态持久化到 pinia(localStorage),刷新后保持用户选择
- 衍生色自动派生:主色的 hover、active、前景色等通过 color-mix 自动派生,仅维护一个主色值,避免多处重复维护
- 变量驱动组件:按钮、页面背景、卡片、文字、边框等全部通过 CSS 变量取色,切换主题即时生效
- 首屏防闪烁:深色模式下刷新页面不出现浅色闪白
视觉要求
- 主题切换控件(深浅切换按钮 + 色板)置于页面顶部,精致、克制
- 色板以圆形色块网格展示,选中态有清晰描边标识
- 组件展示区能直观看到主色与深浅色的联动效果
技术选型
- 前端框架:Vue 3(已有)+ Vite(已有)
- 状态与持久化:Pinia + pinia-plugin-persistedstate(已在 package.json 中,无需新增依赖)
- 样式方案:原生 CSS 变量(design tokens)+
color-mix()派生色,不引入 UI 库、Tailwind/UnoCSS/Less - 无新增任何运行时依赖
实现方案
总体策略
借鉴 Soybean / Vben 的共同设计:CSS 变量是唯一运行时真相。主题切换本质上只做两件事:
- 切换
<html>上的.darkclass(深浅色) - 向
<html>写入--primary变量(主色)
其余所有颜色(衍生色、中性色、固定语义色)都由 tokens.css 静态定义或派生,JS 不散落颜色值。
颜色单源设计
- 固定值全部进 tokens.css:中性色(background/card/foreground/muted/border)与固定语义色(success/warning/destructive)在
:root(浅色)和.dark(深色)两组中集中维护 - 动态值只有
--primary:由 JS 从预设色板写入;--primary-hover、--primary-active、--primary-foreground用color-mix()派生 - 深色模式不再重复维护主色:浅色下 hover 加深(mix black)、深色下 hover 提亮(mix white),在
.dark中仅覆盖派生规则
模块职责
theme/presets.js:预设主色色板(颜色值单源)theme/tokens.css:全部 CSS 变量(浅色 + 深色 + 主色派生规则)theme/index.js:纯函数applyPrimaryColor(color)、applyDarkMode(isDark)stores/app.js:Pinia store,state 为darkMode+primaryColor,watch 变化时调用 theme/index.js 同步到 DOM,persist: true持久化
初始化与防闪烁
- main.js 注册 pinia + 持久化插件,mount 前实例化 app store(此时持久化状态已恢复并 watch 首次触发)
- index.html 的
<head>加 inline script:提前读取 localStorage 中持久化的darkMode,在 CSS 加载前给<html>加.darkclass,避免刷新闪白
性能说明
- 切换操作仅执行 1 次
setProperty+ 1 次classList.toggle,无组件重渲染开销 color-mix()由浏览器一次性计算,无运行时 JS 计算成本;目标为现代浏览器(Vite 8 默认目标,Chrome 111+ 已支持)
架构设计
flowchart LR
A[app store<br/>darkMode + primaryColor] -->|watch| B[theme/index.js]
B -->|classList.toggle dark| C[html.dark]
B -->|setProperty --primary| D[html 变量]
C --> E[tokens.css 深色覆盖]
D --> F[color-mix 派生 hover/active]
E --> G[组件样式 var 取值]
F --> G
H[index.html inline script] -->|提前恢复| C
目录结构
fms-vue/
├── index.html # [MODIFY] head 加 inline script 提前恢复 .dark,防刷新闪烁
└── src/
├── main.js # [MODIFY] 注册 pinia + 持久化插件,mount 前初始化 app store
├── styles/
│ └── index.css # [MODIFY] 顶部 @import '../theme/tokens.css',保留字体配置
├── theme/
│ ├── presets.js # [NEW] 预设主色色板(约 6-8 个精选主色,含默认蓝/青/绿/橙/紫/玫红)
│ ├── tokens.css # [NEW] 浅色 :root + 深色 .dark 全部 CSS 变量 + 主色 color-mix 派生规则
│ └── index.js # [NEW] applyPrimaryColor / applyDarkMode 纯函数,操作 documentElement
├── stores/
│ └── app.js # [NEW] Pinia app store:darkMode/primaryColor + setDark/toggleDark/setPrimary + persist
├── components/
│ └── ui/
│ ├── ThemeToggle.vue # [NEW] 深/浅色切换按钮(太阳/月亮图标 + 动画过渡)
│ ├── ColorThemePicker.vue # [NEW] 预设色板点选组件(圆形色块网格 + 选中描边)
│ └── button/
│ └── button.vue # [MODIFY] 补充基于 tokens 的按钮样式(<style scoped> 引用 var)
└── App.vue # [MODIFY] header 接入 ThemeToggle + ColorThemePicker,补充展示区样式
关键代码结构
presets.js
export const PRESETS = [{ name: '拂晓蓝', color: '#1677ff' }, ...],第一个为默认主色。
stores/app.js(state 与 actions 契约)
- state:
{ darkMode: false, primaryColor: PRESETS[0].color } - actions:
setDark(v)、toggleDark()、setPrimary(color) persist: true(key 默认即 store idapp,与 index.html 读取的 key 一致)
theme/index.js
applyPrimaryColor(color):documentElement.style.setProperty('--primary', color)applyDarkMode(isDark):documentElement.classList.toggle('dark', isDark)
tokens.css 变量骨架
- 中性色:
--background、--card、--foreground、--muted、--muted-foreground、--border - 固定语义色:
--success、--warning、--destructive(浅/深各一组) - 主色系列:
--primary(JS 写入)、--primary-foreground、--primary-hover、--primary-active(color-mix 派生,.dark下覆盖派生方向)
设计风格
采用现代简洁的中后台风格,参考 Soybean / Vben 的克制专业气质。整体轻量、留白充足,主题控件精致不喧宾夺主,切换过程有平滑过渡。
页面结构(App.vue 展示页)
顶部导航栏
- 左侧:品牌标识「FMS UI」+ 页面标题,使用较大的字重形成层次
- 右侧:主题控件区,依次为 ColorThemePicker 色板与 ThemeToggle 深浅切换按钮,间距适度
组件展示区
- 卡片式容器承载 Button 组件展示,卡片背景、边框、文字均随深浅色变化
- 主色按钮(default 类型)作为主题色最直观的反馈载体,切换色板时按钮颜色即时变化
控件设计
ColorThemePicker 色板
- 圆形色块网格排列,每个色块展示预设主色,hover 轻微放大并显示阴影
- 当前选中的色块带外圈描边环 + 内部对勾标识,与浅/深背景均有足够对比度
ThemeToggle 深浅切换
- 胶囊形或圆形按钮,内含太阳/月亮图标,点击在浅色与深色间切换
- 切换采用 icon 旋转/淡入淡出的微动画,状态持久化后刷新不丢失
响应式与可访问性
- 色板与切换按钮在窄屏下自动换行或收缩,保持可点击区域不小于 36px
- 选中态不仅靠颜色区分,还依赖描边与图标双重标识,兼顾色弱用户
Agent Extensions
Skill
- ui-ux-pro-max
- 用途:设计主题切换控件(ColorThemePicker 色板与 ThemeToggle 深浅切换按钮)的视觉方案,确定预设主色色板配色与选中态、hover 态、微动画细节
- 预期产出:一套与中后台风格匹配的控件视觉规范,包含色板颜色选择、控件尺寸/圆角/描边、深浅色下的对比度与可访问性建议