现状
深色模式的样式已经完整实现,但只能跟随系统,用户无法手动选择。
frontend/src/styles/tokens.css 里 :root 定义浅色变量(:6-36),@media (prefers-color-scheme: dark) 覆盖颜色(:40-69)。全 app 没有任何 data-theme / .theme-dark class / classList 操作 —— 没有可供开关挂载的钩子。
frontend/index.html:7-8 的两个 <meta name="theme-color"> 同样是 media 驱动。
现成的参照实现
这个仓库的 site/ 已经做完了这件事,可以直接搬:
app 前端也有一套可复用的状态模式:i18n.tsx 的 Provider + localStorage + try/catch 兜底 + documentElement 副作用(i18n.tsx:321-323 同步 lang 属性,正是主题该挂的位置)。
方案
1. CSS 加钩子
把 tokens.css:40-69 的暗色变量块提取出来,让它同时被媒体查询和显式 class 命中。三态:跟随系统 / 强制浅色 / 强制深色。
注意一个细节:暗色块目前只覆盖颜色变量,--radius-*、--sidebar-width、--footer-height 只在浅色 :root 定义一次。提取时不要把这些尺寸变量一起搬进去,否则强制浅色时它们会丢失。
2. ThemeProvider
照 i18n.tsx 的结构写,复用它的 try/catch 降级(注释说明 hardened webview 里 storage 可能不可用,此时退化为内存态)。
localStorage key 需要定一下命名:site 用 oneagent-theme(连字符),app 的 i18n 用 oneagent.locale(点号)。建议 app 内统一用点号风格 oneagent.theme,与同一个 storage 命名空间下的 locale 保持一致,不必迁就 site(两者不共享 storage)。
3. 防闪烁
Wails app 加载本地资源,闪烁窗口比网页短,但仍需处理:在 index.html 里加 inline script 在 React 挂载前就设好 class。同时 <meta name="theme-color"> 需要跟着手动选择更新,不能只靠 media。
4. 开关放哪
侧边栏底部已经有语言选择器和任务中心,主题开关放在语言选择器旁边最自然 —— 两者都是「应用外观/偏好」类设置。
验收
- 三态可切换,重启应用后保持
- 未做过显式选择时跟随系统,且系统切换时实时响应
- 显式选择后不再被系统偏好覆盖
- 首屏无闪烁
- 强制浅色时尺寸类变量不丢失
- storage 不可用时降级为内存态而非崩溃
- 覆盖测试:三态切换、持久化、系统偏好仅在无显式值时生效
现状
深色模式的样式已经完整实现,但只能跟随系统,用户无法手动选择。
frontend/src/styles/tokens.css 里
:root定义浅色变量(:6-36),@media (prefers-color-scheme: dark)覆盖颜色(:40-69)。全 app 没有任何data-theme/.theme-darkclass /classList操作 —— 没有可供开关挂载的钩子。frontend/index.html:7-8的两个<meta name="theme-color">同样是 media 驱动。现成的参照实现
这个仓库的
site/已经做完了这件事,可以直接搬:document.documentElement.classList.toggle("theme-dark", dark)"oneagent-theme"(ThemeToggle.astro:49)matchMediachange 监听,仅在无显式存储值时才让系统偏好生效aria-pressed同步app 前端也有一套可复用的状态模式:
i18n.tsx的 Provider + localStorage + try/catch 兜底 +documentElement副作用(i18n.tsx:321-323同步lang属性,正是主题该挂的位置)。方案
1. CSS 加钩子
把
tokens.css:40-69的暗色变量块提取出来,让它同时被媒体查询和显式 class 命中。三态:跟随系统 / 强制浅色 / 强制深色。注意一个细节:暗色块目前只覆盖颜色变量,
--radius-*、--sidebar-width、--footer-height只在浅色:root定义一次。提取时不要把这些尺寸变量一起搬进去,否则强制浅色时它们会丢失。2. ThemeProvider
照
i18n.tsx的结构写,复用它的 try/catch 降级(注释说明 hardened webview 里 storage 可能不可用,此时退化为内存态)。localStorage key 需要定一下命名:site 用
oneagent-theme(连字符),app 的 i18n 用oneagent.locale(点号)。建议 app 内统一用点号风格oneagent.theme,与同一个 storage 命名空间下的 locale 保持一致,不必迁就 site(两者不共享 storage)。3. 防闪烁
Wails app 加载本地资源,闪烁窗口比网页短,但仍需处理:在
index.html里加 inline script 在 React 挂载前就设好 class。同时<meta name="theme-color">需要跟着手动选择更新,不能只靠 media。4. 开关放哪
侧边栏底部已经有语言选择器和任务中心,主题开关放在语言选择器旁边最自然 —— 两者都是「应用外观/偏好」类设置。
验收