Skip to content

feat(theme): 暗色主题支持(FConfigProvider theme="dark") - #1044

Open
wanchun wants to merge 4 commits into
mainfrom
feat/dark-mode
Open

wanchun wants to merge 4 commits into
mainfrom
feat/dark-mode

Conversation

@wanchun

@wanchun wanchun commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

背景

此前 FConfigProvider 的 theme prop 是死参数(applyTheme 收了不用),组件背景大量硬编码 var(--f-white),暗色模式无从谈起。本 PR 采用 element-plus 路线(设计令牌全走 CSS 变量、暗色整组翻转)补齐暗色主题。

改动内容

一、表面色语义化(52 处 / 29 个样式文件)

  • 组件背景使用的 var(--f-white) → var(--f-component-bg-color)
  • 「彩底白字」「勾选标记」等 22 处前景用法保留 white(暗色下依然正确)
  • componentBgColor 语义定为组件表面色:亮色 = white,暗色 = #1f1f1f

二、baseTheme 派生链暗色分派(非机械 tint↔shade 互换)

类别 亮色 暗色 理由
灰阶淡化(边框/次级文字/禁用) tint 向白 shade 向黑 梯度方向一致
hover 强调 tint tint 保持 正文已浅,hover 趋近白
active 按下 shade 加深 tint 提亮 加深不可见
fade 透明(遮罩/阴影) fade(fontColorBase) 锚定 #0f1222 避免发灰发亮

布局反转/tooltip 底色暗色下显式定值;亮色路径逐锚点对齐旧实现(回归零差异)。

三、预设与接线

  • 新增 _theme/dark.ts:fontColorBase: #e8ecf2 + bodyBgColor: #141414
  • applyTheme 合并预设,用户 themeOverrides 优先;theme 参数从死参数变为生效
  • FConfigProvider theme="dark" → 60+ CSS 变量整组翻转 + 容器挂 fes-dark class(作用域覆盖钩子)
  • 新增 getThemeStyleTag():SSR 首屏防闪白,从包具名导出

四、文档

configProvider 文档新增「暗色模式」章节(基础用法 / 跟随系统 / SSR 防闪白 / 深度定制)+ 可运行 demo。

验证

  • dark-theme.spec(6 用例):亮色基线无回归、灰阶梯度方向、遮罩锚定、hover 提亮、tooltip 可读性、覆盖优先级
  • dark-mode.spec(7 用例):CSS 变量真实翻转、class 钩子、overrides 优先、dark↔light 切换还原、子组件集成、applyTheme/getThemeStyleTag 单元
  • 全量单测 2539/2539(main 基线 2525 + 新增 14)

已知限制(后续 PR)

  • useTheme 为 shared composable,仅首个调用组件的 Provider 生效(已有 TODO,非本 PR 引入)
  • 部分组件残留 #fff 硬编码(对比报告已列)建议独立 PR 清理

采用 element-plus 路线:设计令牌全部走 CSS 变量,暗色模式整组翻转。

一、表面色语义化(52 处)
- 组件背景使用的 var(--f-white) 统一替换为 var(--f-component-bg-color)
  (29 个组件样式文件;「彩底白字」「勾选标记」等前景用法保留 white,
  暗色下依然正确)
- componentBgColor 语义定为组件表面色,亮色 = white,暗色翻转深灰

二、baseTheme 派生链支持暗色分派
- 派生变量按语义分四类(非机械 tint↔shade 互换):
  · 灰阶淡化系(边框/次级文字/禁用):暗色下 tint→shade 向黑淡出,
    亮度梯度方向与亮色一致
  · hover 强调系:保持 tint 向白提亮(暗色下正文已浅,hover 趋近白)
  · active 按下系:暗色下 shade→tint 反转提亮(加深不可见)
  · fade 透明度系(遮罩/阴影/滚动条):锚定 #0f1222,避免浅色底
    fade 出发灰发亮
- 布局反转/tooltip 底色暗色下显式定值(保持区分度与可读性)
- 亮色路径逐锚点对齐旧实现(回归零差异)

三、暗色预设与接线
- 新增 _theme/dark.ts 预设:fontColorBase 翻浅 + bodyBgColor 深灰
- applyTheme 合并预设(用户 themeOverrides 优先);theme 参数
  从死参数变为生效
- FConfigProvider theme="dark" → 60+ CSS 变量整组翻转;
  容器挂 fes-dark class(无法变量化样式的作用域覆盖钩子)
- 新增 getThemeStyleTag():SSR 首屏防闪白,入口 head 内联
  主题变量 style 标签,从 config-provider 具名导出

四、文档
- configProvider/index.md 新增「暗色模式」章节:基础用法、
  跟随系统偏好、SSR 防闪白、深度定制
- darkTheme.vue 演示(按钮/输入框/选择器/开关暗色渲染)

测试:dark-theme.spec(6 用例:亮色基线无回归/灰阶梯度/
遮罩锚定/hover 提亮/tooltip 可读/覆盖优先级)+
dark-mode.spec(7 用例:变量翻转/class 钩子/覆盖优先级/
主题切换还原/子组件集成/applyTheme 单元/style 标签),
全量 2539/2539 绿
独立 review 发现的三个 P1 问题修复:

一、componentBgColor 亮色回归(P1)
- 重写后亮色路径变成固定 #fff,偏离 main 的
  tint(fontColorBase, 0.97);且用户覆盖 fontColorBase
  不再联动组件底色——主题定制契约破坏
- 修复:亮色恢复 tint 派生链,暗色保持 #1f1f1f 表面

二、carouselColor/carouselHoverColor 别名链断裂(P1)
- main 中二者是 shadowColorSm/maskColor 的别名引用;
  重写改为独立 fade 后,用户覆盖这两个变量不再传导到
  轮播箭头底色
- 修复:提取派生中间值(用户覆盖优先),别名关系恢复

三、LESS 注释 GBK 乱码(P1)
- 批量替换脚本经 PowerShell 管道时把 10 个文件的中文
  注释按 GBK 重编码( irreversible)
- 修复:从 main 逐行恢复原文,仅保留功能性替换
  (var(--f-white) → var(--f-component-bg-color));
  全仓库扫描确认零乱码残留

四、其他
- interface.ts:ThemeCommon 补 maskDarkColor、
  paddingXsmall 命名对齐 base.ts(PR 描述承诺的 38 键对齐)
- docs demo darkTheme.vue:getContainer 限定 demo 容器,
  不再污染整个文档站
- 新增 light-regression.spec:复刻 main 全部派生公式逐变量
  断言零差异 + 别名传导用例

全量 2541/2541 通过
@wanchun

wanchun commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator Author

Review 修复(第二轮独立 review 后)

1. componentBgColor 亮色回归

亮色路径恢复 main 的 tint(fontColorBase, 0.97) 派生(暗色保持 #1f1f1f)。用户覆盖 fontColorBase 继续联动组件底色,52 处替换点亮色输出与 main 一致。

2. carouselColor / carouselHoverColor 别名链断裂

恢复 main 的别名关系(shadowColorSm / maskColor),用户覆盖这两个变量继续传导到轮播箭头底色。新增别名传导回归用例。

3. LESS 注释乱码恢复

批量替换脚本经 PowerShell 管道时产生的 GBK 乱码(10 个文件),全部从 main 逐行恢复原文,仅保留功能性替换。全仓库扫描确认零残留。

4. 其他

  • interface.ts:ThemeCommon 补 maskDarkColor、paddingXsmall 命名对齐(落实 PR 描述承诺)
  • docs demo darkTheme.vue:getContainer 限定 demo 容器,不再污染整个文档站
  • 新增 light-regression.spec.ts:复刻 main 全部派生公式逐变量断言零差异 + 别名传导用例(覆盖 41 个派生变量)

全量 2541/2541 通过

遗留 P2(建议后续 issue 跟进)

  • resolveThemePreset 仅精确匹配 'dark'('auto' 可作为后续扩展点)
  • getThemeStyleTag selector 未转义(当前来源固定,理论风险)
  • useTheme 共享单例在多 app / 嵌套 provider 场景的限制(见代码 TODO)

第三轮 review 发现:上次修复 inputInner.less:14 乱码注释时,
行尾混入 1 个不可见私用区字符 U+E787(乱码字节残留)。
字节级修剪,该文件相对 main 现在仅剩纯功能性替换行
(background-color 的 var(--f-white) -> var(--f-component-bg-color))。
独立 review 第四轮发现的三项修复:

一、maskDarkColor / borderColorBase 恢复 main 的可覆盖语义
- main 里这两个变量位于 getDefaultThemeBase(可被
  themeOverrides.common 覆盖);重写后移入无条件派生区,
  深度定制暗色遮罩/边框的用户覆盖失效
- 修复:仿 shadowColorSm 模式(base 值优先 + ?? 派生兜底);
  borderBase 字符串模板同步引用覆盖后的 borderColorBase
- 新增覆盖传导回归用例(含 borderBase 联动断言)

二、interface.ts derivedColor 补 4 个缺失声明
- hoverSuccessTextColor / hoverWarningTextColor /
  hoverDangerTextColor / hoverBaseTextColor
- baseTheme 输出 40 个派生 key 现全部有类型声明
- vue-tsc --noEmit 通过

三、dropdown / layout / menu 三文件漏网乱码恢复(5 行)
- 第三轮已发现、修复滞留工作区(跨分支操作事故),本轮
  字节级验证后正式提交:从 main 同行号恢复中文注释原文,
  保留 var(--f-white) -> var(--f-component-bg-color) 替换

定向 55/55(_theme/config-provider/image)+ tsc 通过;
全分支乱码/PUA 终扫 CLEAN
@wanchun

wanchun commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator Author

第四轮独立 review 修复(26a531b1)

1. maskDarkColor / borderColorBase 恢复 main 的可覆盖语义

review 发现这两个变量在 main 位于 getDefaultThemeBase(可被 themeOverrides.common 覆盖),重写后被移入无条件派生区导致覆盖失效。已按 shadowColorSm 同款模式修复(base 值优先 + ?? 派生兜底),borderBase 字符串模板同步引用覆盖后值。新增覆盖传导回归用例。

2. interface.ts derivedColor 补 4 个缺失声明

hoverSuccessTextColor / hoverWarningTextColor / hoverDangerTextColor / hoverBaseTextColor——baseTheme 全部 40 个派生 key 现有类型声明。vue-tsc 通过。

3. dropdown / layout / menu 漏网乱码恢复(5 行注释)

第三轮已发现的三文件乱码,从 main 字节级恢复。全分支乱码/PUA 终扫 CLEAN。

全量 2542 用例:2541 绿 + 1 个已知 flake(onOk reject 节流轮询,单跑 13/13 绿)


四轮 review 汇总

轮次 发现 状态
一 componentBgColor 亮色回归、别名断裂、乱码 ×10 ✅ fa5c4a1
二 IME 守卫(aria 侧)、inputInner 脏字节、漏网乱码 ×3 ✅ 9338de0 / 26a531b
三 破坏性验证全部 P1 修复真实有效;亮色 65 key 零差异 ✅ 通过
四 maskDarkColor/borderColorBase 覆盖收窄、接口缺 4 key、乱码 ×3 提交 ✅ 26a531b

可合。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant