# Halo 黑暗模式插件 — 设计文档
> 版本:v0.2.0-draft(基于 create-halo-plugin + dev-skills 调查更新)
> 日期:2026-08-06
> 状态:待审阅
> 上一步:[调查文档](./调查文档.md)(含 0.4 节补充调查更新)
---
## 目录
1. [设计目标与范围](#1-设计目标与范围)
2. [技术架构](#2-技术架构)
3. [CSS 变量体系设计](#3-css-变量体系设计)
4. [黑暗模式调色板](#4-黑暗模式调色板)
5. [组件覆盖策略](#5-组件覆盖策略)
6. [切换器 UI 设计](#6-切换器-ui-设计)
7. [路由与菜单](#7-路由与菜单)
8. [偏好持久化](#8-偏好持久化)
9. [项目文件结构](#9-项目文件结构)
10. [实现阶段划分](#10-实现阶段划分)
11. [测试策略](#11-测试策略)
12. [兼容性矩阵](#12-兼容性矩阵)
---
## 1. 设计目标与范围
### 1.1 核心目标
将 Halo 后台管理面板(Console)从纯浅色模式改造为支持浅色/黑暗双模式,**不修改 Halo 核心代码**,完全通过插件机制实现。
### 1.2 范围界定
| 范围 | 包含 | 不包含 |
|------|------|--------|
| 页面 | Halo Console(后台管理)全体页面 | 用户中心 (uc-src)、前台主题 |
| 组件 | Halo 核心组件 + `@halo-dev/components` 组件库 | 第三方插件自有 UI |
| 编辑器 | FormKit 表单 + 富文本编辑器 | 编辑器内容区自定义样式 |
| 模式 | 浅色 ↔ 黑暗手动切换 + 跟随系统 | 定时切换、多主题 |
### 1.3 非功能性目标
- **性能**:CSS 变量切换应 < 50ms,无可见闪烁(FOUC)
- **可访问性**:黑暗模式下所有文本满足 WCAG AA 对比度要求(≥ 4.5:1)
- **兼容性**:支持 Halo ≥ 2.23.0(对应 plugin-starter 的版本约束)
- **可维护性**:CSS 变量体系命名清晰,一个语义变量对应一个视觉属性
### 1.4 反目标(明确不做)
- ❌ 不美化 UI(不改变布局、圆角、间距、字体等)
- ❌ 不添加任何视觉装饰效果
- ❌ 不修改 Halo 组件库源码
- ❌ 不支持前台主题的暗色化
---
## 2. 技术架构
### 2.1 整体架构图
```
┌──────────────────────────────────────────────────────────┐
│ 插件边界 │
│ │
│ ┌─────────────┐ ┌──────────────────────────────────┐ │
│ │ Java 后端 │ │ 前端 (ui/) │ │
│ │ │ │ │ │
│ │ BasePlugin │ │ ┌────────────────────────────┐ │ │
│ │ ├ start() │ │ │ index.ts (definePlugin) │ │ │
│ │ └ stop() │ │ │ ├ components: { │ │ │
│ │ │ │ │ │ ThemeToggle │ │ │
│ │ (极简骨架) │ │ │ │ } │ │ │
│ └─────────────┘ │ │ ├ routes: [设置页面] │ │ │
│ │ │ └ extensionPoints: {} │ │ │
│ │ └────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ composables/ │ │ │
│ │ │ ├ useDarkMode.ts │ │ │
│ │ │ └ useSystemPreference.ts │ │ │
│ │ └────────────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────────────┐ │ │
│ │ │ styles/ │ │ │
│ │ │ ├ variables.css │ │ │
│ │ │ ├ dark-theme.css │ │ │
│ │ │ ├ overrides/ │ │ │
│ │ │ │ ├ layout.css │ │ │
│ │ │ │ ├ components.css │ │ │
│ │ │ │ ├ formkit.css │ │ │
│ │ │ │ ├ editor.css │ │ │
│ │ │ │ └ scrollbar.css │ │ │
│ │ │ └ index.css │ │ │
│ │ └────────────────────────────┘ │ │
│ └──────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 注入方式(Halo 插件加载机制自动处理) │ │
│ │ CSS → /apis/.../ui-plugins/-/bundle.css │ │
│ │ JS → /apis/.../ui-plugins/-/bundle.js │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
```
### 2.2 运行时数据流
```
┌──────────────┐
│ App 启动 │
└──────┬───────┘
│
┌──────▼───────┐
│ 读取持久化偏好 │
│ localStorage │
│ (默认: system)│
└──────┬───────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 浅色 │ │ 黑暗 │ │ 跟随系统 │
│ theme= │ │ theme= │ │ theme= │
│ "light" │ │ "dark" │ │ "auto" │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
│ │ ┌─────▼──────┐
│ │ │ 监听 match │
│ │ │ Media query│
│ │ └─────┬──────┘
│ │ │
└────────────┼────────────┘
│
┌──────▼───────┐
│ 设置 │
│ document │
│ .documentEl │
│ 的 data attr │
│ data-halo- │
│ theme="dark" │
│ 或移除该属性 │
└──────┬───────┘
│
┌──────▼───────┐
│ CSS 变量切换 │
│ :root vs │
│ [data-halo- │
│ theme="dark"]│
└──────────────┘
```
### 2.3 关键技术决策
| 决策点 | 选择 | 理由 |
|--------|------|------|
| 主题切换方式 | `data-halo-theme` 属性 | 前缀避免冲突,属性选择器高效 |
| 颜色系统 | CSS Variables + OKLCH 颜色空间 | 感知均匀,暗色模式天然适配 |
| 切换状态管理 | Vue composable (`useDarkMode`) | 轻量,无 Pinia 依赖,方便跨组件复用 |
| 持久化存储 | `localStorage` | 极简,零后端依赖,立即可用 |
| 系统偏好监听 | `matchMedia('prefers-color-scheme: dark')` | 标准 API,所有现代浏览器支持 |
| 初始加载防闪烁 | `
```
**注意**:由于插件 bundle 是异步加载的(`useScriptTag`),FOUC 风险需要通过以下方式缓解:
- CSS 变量切换是瞬时的(< 1 帧),即使有短暂浅色闪现,用户体感为"页面加载完成"
- 后续可通过向 Halo 提交 PR 在 `console.html` 添加 `