Skip to content

主题系统 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 简介
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与可维护性
  8. 故障排查指南
  9. 结论
  10. 附录:主题开发规范与迁移流程

简介 ​

本文件系统性梳理 AI Helper 的主题体系,围绕以下目标展开:

  • 解释 CSS 变量 Token 体系的设计原理与组织结构(颜色、字体、间距、阴影、品牌色等)。
  • 说明亮/暗/跟随系统三态主题的检测算法、状态管理与持久化机制。
  • 给出暗黑模式合规审计工具的使用方法,包括对比度检查、白斑检测与自动化测试流程。
  • 提供主题开发指南:新增 Token、样式编写规范、兼容性要求与自定义主题实践。
  • 说明主题迁移工具与版本升级的处理方式,确保从硬编码色值到 Token 的平滑演进。

项目结构 ​

主题相关代码集中在共享层与脚本层:

  • 共享层:主题 Token 定义与运行时切换逻辑。
  • 脚本层:静态门禁、动态审计与批量迁移工具。
  • 扩展端页面:选项页与侧边栏在初始化时订阅并应用主题。
graph TB
subgraph "共享层"
TCSS["theme.css<br/>Token 定义"]
TJS["theme.js<br/>主题运行时"]
end
subgraph "扩展端页面"
OPT["options/index.js<br/>选项页入口"]
SP["side_panel/index.js<br/>侧边栏入口"]
end
subgraph "脚本层"
AUDIT["theme-dark-audit.mjs<br/>动态审计"]
CONTRAST["theme-contrast-check.py<br/>静态对比度门禁"]
MIGRATE["theme-migrate.py<br/>批量迁移"]
MAP["theme-map.json<br/>映射表"]
end
TCSS --> OPT
TCSS --> SP
TJS --> OPT
TJS --> SP
CONTRAST --> TCSS
AUDIT --> TJS
MIGRATE --> MAP
MIGRATE --> TCSS

图表来源

章节来源

核心组件 ​

  • 主题 Token 定义(CSS):集中声明亮/暗两套语义化变量,覆盖表面、文本、品牌、边框、状态、浮层、阴影、滚动条、选择高亮等。
  • 主题运行时(JS):管理偏好(light/dark/system)、生效值(light/dark)、防闪烁镜像、媒体查询监听、跨标签页存储同步与订阅通知。
  • 静态门禁(Python):校验暗色 Token 对比度是否达标,拦截不合规变更。
  • 动态审计(Playwright):加载真实扩展产物,遍历已渲染元素,检测对比度违规与“白斑”泄漏。
  • 迁移工具(Python + JSON):将硬编码色值替换为 var(--token),并提供白名单、上下文分发族与灰阶/黑白带策略。

章节来源

架构总览 ​

主题系统采用“CSS Token + JS 运行时 + 静态/动态门禁 + 迁移工具”的分层设计:

  • 样式层仅引用 Token,禁止硬编码色值。
  • 运行时通过 <html data-theme> 驱动 CSS 变量翻转。
  • 门禁保障 Token 质量与一致性。
  • 迁移工具降低历史遗留成本。
sequenceDiagram
participant User as "用户"
participant Options as "选项页"
participant ThemeJS as "theme.js"
participant Storage as "chrome.storage.local"
participant CSS as "theme.css"
participant Audit as "theme-dark-audit.mjs"
User->>Options : 选择主题(light/dark/system)
Options->>ThemeJS : setTheme(pref)
ThemeJS->>Storage : 写入 appTheme
ThemeJS->>ThemeJS : resolve(pref)
ThemeJS->>ThemeJS : apply(resolved)
ThemeJS->>CSS : 设置 <html data-theme>
Note over CSS : 暗色块覆盖 Token 值
Audit->>ThemeJS : 读取 storage.appTheme
Audit->>Audit : 遍历已渲染元素计算对比度/白斑
Audit-->>User : 输出审计结果(通过/失败)

图表来源

详细组件分析 ​

主题 Token 体系(CSS) ​

  • 组织方式
    • :root 定义亮色 Token;[data-theme="dark"] 定义暗色 Token。
    • 分类清晰:表面、文本、品牌、边框分隔、状态(危险/成功/警告/信息)、Toast 底色、实心填充、蒙层/阴影/杂项、固定不翻转(纸面/代码块)。
    • 提供 RGB 通道 Token(如 --brand-rgb、--overlay-rgb),便于透明度组合与阴影复用。
  • 关键设计点
    • 浮层 elevation 由 ring + overlay-rgb + bg-elevated 组合,随主题自适应。
    • 原生控件 accent-color 统一使用品牌色,避免 UA 默认色破坏主题。
    • Markdown 在暗色下逐类覆盖,保证可读性与一致性。
  • 复杂度与可扩展性
    • Token 命名遵循语义化前缀,新增类别只需在两个块中成对补充。
    • 通过 RGB 通道与 var() 组合减少重复色值,提升一致性。
classDiagram
class ThemeTokens {
"+表面 : --bg-page, --bg-surface, --bg-elevated, --bg-subtle, --bg-sunken, --bg-disabled, --bg-hover, --bg-active, --surface-rgb"
"+文本 : --text-primary, --text-secondary, --text-tertiary, --text-disabled, --text-inverse, --text-link, --text-on-brand"
"+品牌 : --brand-500/600/700, --brand-fill, --brand-accent, --brand-violet-500/600, --brand-soft-bg/border, --brand-rgb/violet-rgb/accent-rgb, --brand-gradient"
"+边框分隔 : --border-subtle/default/strong, --divider"
"+状态 : danger/success/warning/info (500/strong/soft-bg/border/text)"
"+浮层阴影 : --scrim, --overlay-rgb, --shadow-sm/md/lg, --elev-ring/pop/float/surface, --tooltip-bg, --scrollbar-thumb, --selection-bg"
"+固定不翻转 : --mark-bg, --paper, --code-bg, --code-fg"
}

图表来源

章节来源

主题运行时(JS) ​

  • 状态模型
    • preference:用户偏好(light | dark | system),持久化于 chrome.storage.local 的 appTheme。
    • resolved:生效值(light | dark),写入 <html data-theme> 供 CSS 变量翻转。
  • 防闪烁
    • 模块顶层同步读取 localStorage 镜像 appThemeResolved,先落一次属性,再异步 initTheme 覆盖。
  • 系统主题跟随
    • 监听 matchMedia('(prefers-color-scheme: dark)'),当 preference=system 时自动切换 resolved。
  • 跨标签同步
    • 监听 chrome.storage.onChanged,当其他标签修改 appTheme 时本地更新并触发订阅者。
  • 订阅机制
    • subscribeTheme(cb) 收集回调,resolved 变化时调用,用于重绘 Mermaid 等依赖主题的元素。
flowchart TD
Start(["模块加载"]) --> Cache["读取 localStorage.appThemeResolved"]
Cache --> ApplyCache{"有缓存?"}
ApplyCache --> |是| SetAttr["documentElement.setAttribute('data-theme', cached)"]
ApplyCache --> |否| WaitInit["等待 initTheme()"]
SetAttr --> WaitInit
WaitInit --> MQ["注册 prefers-color-scheme 监听"]
MQ --> Storage["读取 chrome.storage.local.appTheme"]
Storage --> Resolve["resolve(preference)"]
Resolve --> Apply["apply(resolved)"]
Apply --> Notify["通知订阅者"]
Notify --> End(["就绪"])

图表来源

章节来源

静态对比度门禁(Python) ​

  • 功能
    • 解析 theme.css 的 :root 与 [data-theme="dark"] 块,按 WCAG 1.4.3 计算对比度。
    • 文本类阈值 4.5:1,图形/强调类 3:1,装饰性分隔仅 INFO 提示。
    • 未达标即非零退出,阻止不合规 Token 进入分支。
  • 输入/输出
    • 输入:theme.css 路径。
    • 输出:[AA FAIL] 或 [INFO] 行,最后汇总未达标数量。
flowchart TD
A["读取 theme.css"] --> B["解析暗色块 tokens"]
B --> C["识别文本/图形/装饰性分类"]
C --> D["计算相对亮度与对比度"]
D --> E{"是否低于阈值?"}
E --> |是| F["输出 [AA FAIL]"]
E --> |否| G["跳过或输出 [INFO]"]
F --> H["返回非零退出码"]
G --> I["返回零退出码"]

图表来源

章节来源

动态暗黑审计(Playwright) ​

  • 功能
    • 启动 Chromium 加载 dist 扩展,将侧边栏与配置页切到 dark。
    • 逐个面板/Tab 展开,遍历所有已渲染元素,实测前景/背景(含 alpha 合成到 --bg-page 底)。
    • 检测两类问题:WCAG 对比度不达标、暗色下近纯白大块背景(白斑)。
    • 注入 Toast/弹窗表单控件进行瞬态回归防护。
  • 运行方式
    • 需先 npm run build:silent。
    • 可选 --keep 保留浏览器以便人工核对。
    • 存在违规或 pageerror 则 exit 1。
sequenceDiagram
participant CLI as "命令行"
participant PW as "Playwright"
participant Ext as "dist 扩展"
participant Page as "side_panel.html / options.html"
participant Auditor as "页内审计函数"
CLI->>PW : 启动并加载扩展
PW->>Ext : 安装 dist
PW->>Page : 打开侧边栏/配置页
PW->>Page : 设置 storage.appTheme='dark'
PW->>Auditor : evaluate(AUDIT_FN)
Auditor-->>PW : violations + blotches
PW-->>CLI : 汇总报告 + 退出码

图表来源

章节来源

主题迁移工具(Python + JSON) ​

  • 能力
    • 扫描 CSS/JS 中的十六进制色值与 rgba 三元组。
    • 根据 theme-map.json 的 direct/contextDispatch/rgbaFamilies/bands/whitelist 规则替换为 var(--token)。
    • 支持 --check/--missing/--missing-rgba/--ranges/--mode js 等模式。
    • 对 white/black 关键字按上下文智能映射(文字用 on-brand,背景用 surface)。
  • 关键策略
    • 黑/白带:black 透明叠加走 overlay-rgb/shadow-*,white 半透字保持字面量,白底转 surface-rgb。
    • 灰阶:灰色三元组统一走 overlay-rgb。
    • 白名单:全局豁免与文件作用域豁免并存,避免误报。
  • 工作流
    • 先用 --check 发现裸色;再用 apply 批量替换;必要时 review 列表人工复核。
flowchart TD
S["输入文件集"] --> M["加载 theme-map.json"]
M --> Scan["扫描 hex/rgba/关键字"]
Scan --> Map["匹配 direct/contextDispatch/rgbaFamilies/bands"]
Map --> WL{"是否在白名单?"}
WL --> |是| Keep["保持原值"]
WL --> |否| Replace["替换为 var(--token)"]
Replace --> Review{"是否需要人工复核?"}
Review --> |是| List["输出 review 列表"]
Review --> |否| Done["完成"]
Keep --> Done

图表来源

章节来源

页面集成与主题 UI ​

  • 选项页
    • 初始化时优先调用 initTheme(),防止闪烁。
    • 外观主题三分段单选框,change 事件调用 setTheme 持久化并即时翻转。
    • 订阅主题变化以同步 UI 选中状态。
  • 侧边栏
    • 初始化时同样优先 initTheme(),并订阅主题变化以重绘 Mermaid 等依赖主题的元素。
    • 提供主题切换入口,调用 setTheme 并刷新。
sequenceDiagram
participant Opt as "options/index.js"
participant SP as "side_panel/index.js"
participant Theme as "theme.js"
Opt->>Theme : initTheme()
Opt->>Theme : subscribeTheme(syncUI)
SP->>Theme : initTheme()
SP->>Theme : subscribeTheme(repaintMermaid)
Opt->>Theme : setTheme(radio.value)
SP->>Theme : setTheme(value)

图表来源

章节来源

依赖关系分析 ​

  • 耦合关系
    • 页面入口依赖 theme.js 提供的 initTheme/setTheme/subscribeTheme。
    • theme.css 被所有页面样式消费,但不应直接引入色值。
    • 脚本层工具相互独立:contrast-check 仅读 theme.css;audit 依赖 Playwright 与 dist;migrate 依赖 theme-map.json。
  • 外部依赖
    • Chrome API:chrome.storage.local、chrome.storage.onChanged。
    • Web API:matchMedia、localStorage。
    • Node/Playwright:审计脚本运行环境。
graph LR
OPT["options/index.js"] --> THEME["shared/theme.js"]
SP["side_panel/index.js"] --> THEME
THEME --> STORAGE["chrome.storage.local"]
THEME --> MQ["matchMedia"]
THEME --> LS["localStorage"]
CONTRAST["theme-contrast-check.py"] --> CSS["shared/theme.css"]
AUDIT["theme-dark-audit.mjs"] --> THEME
MIGRATE["theme-migrate.py"] --> MAP["theme-map.json"]

图表来源

章节来源

性能与可维护性 ​

  • 性能
    • 主题切换仅在 resolved 真正变化时触发订阅者,避免无意义重绘。
    • 防闪烁镜像减少首屏闪烁,提升用户体验。
    • 静态门禁在 CI 阶段执行,不影响运行时。
  • 可维护性
    • Token 语义化命名与分层,新增/调整成本低。
    • 迁移工具与白名单机制降低历史债务清理成本。
    • 动态审计覆盖瞬态 UI(Toast/弹窗),提高回归质量。

[本节为通用指导,不涉及具体文件分析]

故障排查指南 ​

  • 症状:暗色下文字不可读
    • 排查步骤
      • 运行静态对比度门禁:python3 scripts/theme-contrast-check.py src/shared/theme.css。
      • 运行动态审计:node scripts/theme-dark-audit.mjs,查看 violations 列表。
      • 检查是否使用了未 Token 化的色值:python3 scripts/theme-migrate.py --check src/side_panel/styles.css src/options/styles.css。
  • 症状:暗色下出现大面积白底(白斑)
    • 排查步骤
      • 使用动态审计 blotches 定位具体选择器与尺寸。
      • 检查是否错误地将白色作为背景色,应改为 var(--bg-surface) 或 surface-rgb。
  • 症状:主题切换后 Mermaid 未更新
    • 排查步骤
      • 确认 side_panel/index.js 已订阅主题变化并重绘 Mermaid。
      • 检查 theme.js 的 subscribeTheme 回调是否被正确调用。

章节来源

结论 ​

本主题系统通过 Token 化、运行时三态切换、静态/动态门禁与迁移工具,构建了高质量、可演进、可审计的主题基础设施。建议在后续开发中严格遵循 Token 化规范,持续使用门禁与审计保障暗色体验,并通过迁移工具逐步消除历史硬编码色值。

[本节为总结性内容,不涉及具体文件分析]

附录:主题开发规范与迁移流程 ​

新增 Token 的步骤 ​

  • 在 theme.css 的 :root 与 [data-theme="dark"] 中成对添加语义化 Token。
  • 若涉及透明度组合,新增对应 RGB 通道 Token(如 --xxx-rgb)。
  • 运行静态对比度门禁,确保暗色 Token 达标。
  • 在 theme-map.json 中补充 direct/contextDispatch/rgbaFamilies 映射(如需)。
  • 使用迁移工具扫描并替换旧色值,必要时人工复核 review 列表。

章节来源

样式编写规范 ​

  • 只允许引用 theme.css 中的 Token,禁止硬编码色值。
  • 背景色使用 --bg-surface / --bg-elevated / --bg-subtle 等语义 Token。
  • 文字色使用 --text-primary / --text-secondary / --text-tertiary。
  • 品牌强调使用 --brand-500/600/700 与 --brand-accent。
  • 状态色使用 --danger-* / --success-* / --warning-* / --info-*。
  • 浮层与阴影使用 --elev-* / --shadow-* / --scrim / --overlay-rgb。
  • 原生控件 accent-color 使用 --brand-500。

章节来源

在组件中使用主题变量 ​

  • 示例路径(不展示代码内容)
  • 实践要点
    • 页面初始化时调用 initTheme(),并在 change 事件中调用 setTheme()。
    • 使用 subscribeTheme 监听主题变化,重绘依赖主题的元素(如 Mermaid)。

章节来源

创建自定义主题 ​

  • 方案一:基于现有 Token 调整
    • 在 theme.css 中覆盖特定 Token 的值(注意同时维护亮/暗两块)。
    • 运行对比度门禁与动态审计验证效果。
  • 方案二:扩展新 Token 族
    • 新增语义 Token 与 RGB 通道,完善映射表与迁移规则。
    • 在页面中按需引用新 Token。

章节来源

主题迁移工具使用指南 ​

  • 常用命令
    • 检查裸色:python3 scripts/theme-migrate.py --check src/side_panel/styles.css src/options/styles.css
    • 缺失 Token 清单:python3 scripts/theme-migrate.py --missing src/side_panel/styles.css
    • 缺失 rgba 三元组:python3 scripts/theme-migrate.py --missing-rgba src/side_panel/styles.css
    • 批量替换:python3 scripts/theme-migrate.py src/side_panel/styles.css src/options/styles.css
    • 限定行段:--ranges "120-340,500-780"
    • JS 模式:--mode js
  • 白名单策略
    • 全局豁免与文件作用域豁免并存,谨慎使用,避免掩盖真实漏网。

章节来源

自动化测试与门禁集成 ​

  • package.json 脚本
    • theme:check:静态门禁(迁移工具)
    • theme:contrast:静态门禁(对比度)
    • theme:audit:动态审计(Playwright)
  • 建议流程
    • 提交前运行 npm run build:silent。
    • 依次执行 theme:contrast、theme:check、theme:audit。
    • 全部通过后提交。

章节来源