Skip to content

历史项目主题换肤升级指南

本文档提供将历史项目升级为支持主题换肤的完整操作指南,涵盖硬编码色值替换、旧 CSS 变量迁移、按钮色改造三大步骤。

目录


一、升级前准备

1.1 备份项目

bash
# 创建备份分支
git checkout -b backup/theme-upgrade-$(date +%Y%m%d)
git push origin backup/theme-upgrade-$(date +%Y%m%d)

# 创建升级工作分支
git checkout -b feat/theme-upgrade

1.2 确认需处理的文件范围

bash
# 列出所有样式文件(.scss / .css / .less)
find src -type f \( -name "*.scss" -o -name "*.css" -o -name "*.less" \) | grep -v node_modules

# 列出所有 TSX / JSX 文件
find src -type f \( -name "*.tsx" -o -name "*.jsx" \) | grep -v node_modules

1.3 排除项

以下文件类型 不纳入替换范围

  • *.test.**.spec.*__tests__/ 目录下的测试文件
  • node_modules/ 目录
  • 已注释行
  • 注释块中的内容

二、步骤一:硬编码色值替换

遍历当前项目源码,按以下规则将硬编码色值替换为 CSS 变量写法。

2.1 替换规则对照表

匹配总则(替换前必须遵守,优先级高于下方所有操作要求):

  1. 完全匹配优先:源码色值与下表列出的色值逐字符完全匹配(大小写不敏感)时 → 直接按对应规则替换。
  2. 近似匹配(仅以 #0564f5 为基准,1-2 色阶差):源码色值虽未在表中列出,但与主品牌基准色 #0564f5(rgb(5, 100, 245))差异极小(≤ 2 色阶)时 → 推断为主品牌色近似变体,替换为 var(--brand-color)(含透明度时用 var(--brand-color-rgb)),并在报告中标记 [近似匹配] 供人工复核。近似匹配只与 #0564f5 比较,不与表中其他色值比较;表中其余色值仅参与完全匹配。判定阈值:设 Δ_ch = max(|ΔR|,|ΔG|,|ΔB|)、欧氏距离 d,1 色阶差为 Δ_ch ≤ 16 且 d ≤ 25,2 色阶差为 Δ_ch ≤ 32 且 d ≤ 50,两者需同时满足。
  3. 超阈值一律保留:与 #0564f5 差异超出近似阈值(单通道 > 32 或欧氏距离 > 50)→ 保留原样,不替换。
  4. 禁止基于色名自动扩展:不得基于色名、变量名(含 primaryblue)或上下文推断替换目标;替换必须由色值本身的匹配结果驱动。
  5. 替换目标固定:本步骤所有命中色值仅替换为 var(--brand-color) / var(--brand-color-hover)(含 -rgb 伴侣变量),不做基于属性名的语义化变量推断(语义化变量用于新组件开发,见 如何支持主题换肤功能)。

主品牌色 → var(--brand-color)

硬编码色值替换为
#0564f5var(--brand-color)
#0564F5var(--brand-color)
rgb(5, 100, 245)var(--brand-color)
rgb(5,100,245)var(--brand-color)
#096dd9var(--brand-color)
#096DD9var(--brand-color)
rgb(9, 109, 217)var(--brand-color)
rgb(9,109,217)var(--brand-color)
#2065cfvar(--brand-color)
#2065CFvar(--brand-color)
rgb(32, 101, 207)var(--brand-color)
rgb(32,101,207)var(--brand-color)
#1d77ffvar(--brand-color)
#1D77FFvar(--brand-color)
rgb(29, 119, 255)var(--brand-color)
rgb(29,119,255)var(--brand-color)
#1677ff / #1677FF(Ant Design v5 主色)var(--brand-color)
rgb(22, 119, 255) / rgb(22,119,255)var(--brand-color)
#0052d9 / #0052D9var(--brand-color)
rgb(0, 82, 217) / rgb(0,82,217)var(--brand-color)
#2b7fff / #2B7FFFvar(--brand-color)
rgb(43, 127, 255) / rgb(43,127,255)var(--brand-color)
#0060df / #0060DFvar(--brand-color)
rgb(0, 96, 223) / rgb(0,96,223)var(--brand-color)
#1a7aff / #1A7AFFvar(--brand-color)
rgb(26, 122, 255) / rgb(26,122,255)var(--brand-color)

次品牌色 → var(--brand-color-hover)

硬编码色值替换为
#4e80f5var(--brand-color-hover)
#4E80F5var(--brand-color-hover)
rgb(78, 128, 245)var(--brand-color-hover)
rgb(78,128,245)var(--brand-color-hover)
#1890ffvar(--brand-color-hover)
#1890FFvar(--brand-color-hover)
rgb(24, 144, 255)var(--brand-color-hover)
rgb(24,144,255)var(--brand-color-hover)
#40a9ffvar(--brand-color-hover)
#40A9FFvar(--brand-color-hover)
rgb(64, 169, 255)var(--brand-color-hover)
rgb(64,169,255)var(--brand-color-hover)
#3886fbvar(--brand-color-hover)
#3886FBvar(--brand-color-hover)
rgb(56, 134, 251)var(--brand-color-hover)
rgb(56,134,251)var(--brand-color-hover)
#238dffvar(--brand-color-hover)
#238DFFvar(--brand-color-hover)
rgb(35, 141, 255)var(--brand-color-hover)
rgb(35,141,255)var(--brand-color-hover)
#3783ffvar(--brand-color-hover)
#3783FFvar(--brand-color-hover)
rgb(55, 131, 255)var(--brand-color-hover)
rgb(55,131,255)var(--brand-color-hover)
#4096ff / #4096FF(Ant Design v5 hover)var(--brand-color-hover)
rgb(64, 150, 255) / rgb(64,150,255)var(--brand-color-hover)
#5b8ff9 / #5B8FF9var(--brand-color-hover)
rgb(91, 143, 249) / rgb(91,143,249)var(--brand-color-hover)
#69c0ff / #69C0FFvar(--brand-color-hover)
rgb(105, 192, 255) / rgb(105,192,255)var(--brand-color-hover)
#4ca1dc / #4CA1DCvar(--brand-color-hover)
rgb(76, 161, 220) / rgb(76,161,220)var(--brand-color-hover)

2.2 替换操作要求

要求 1:变量名保护

替换前需判断目标色值是否出现在类名/变量名中,若是则跳过。例如:

  • text-[#0564f5]跳过(类名中含有色值)
  • bg-[#0564f5]跳过
  • border-[#4e80f5]跳过
  • $blue-base: #096dd9跳过(SASS 变量名含 blue
  • @blue-6: #1890ff跳过(Less 变量名含 blue

要求 2:注释保留原有写法

文件类型处理方式
.scss / .less使用 // 单行注释保留原有写法
.css使用 /* */ 注释保留原有写法

示例(.scss 文件)

scss
// ❌ 替换前
.button {
  background-color: #0564f5;
}

// ✅ 替换后
.button {
  // background-color: #0564f5;  // [*] 已替换为 var(--brand-color)
  background-color: var(--brand-color);
}

示例(.css 文件)

css
/* ❌ 替换前 */
.button {
  background-color: #0564f5;
}

/* ✅ 替换后 */
.button {
  /* background-color: #0564f5; */ /* [*] 已替换为 var(--brand-color) */
  background-color: var(--brand-color);
}

要求 3:跳过测试文件和已注释行

  • *.test.**.spec.*__tests__/ 目录下的文件直接跳过
  • 已被注释的行(///* */直接跳过

要求 4:以下场景跳过不替换

场景说明示例
在注释中色值位于注释内容中// 背景色 #0564f5 → 跳过
ECharts 属性作为 echart 图表的属性值color: '#0564f5' 在 echart option 中 → 跳过
颜色数组中色值位于数组字面量内['#0564f5', '#4e80f5'] → 跳过
var() 默认值作为 var() 的回退值var(--custom-color, #0564f5) → 跳过
class / 变量名含 blueclass 名或 SASS/Less 变量名包含 blue 关键字.text-blue-500 { color: #0564f5 } → 跳过
$blue-base: #096dd9 → 跳过
@blue-6: #1890ff → 跳过
取色器 / 调色板对象色值作为对象属性值,且该对象所有属性值都是色值(hex / rgb / rgba)→ 认定为取色器对象,其中所有色值整体跳过{ primary: '#0564f5', hover: '#4e80f5' } → 跳过

ECharts 场景最终方案:不替换 ECharts option 中的硬编码色值。ECharts 需要使用 getCssVarHex('--brand-color') 将 CSS 变量转为 hex 值后传入,参阅 如何支持主题换肤功能

取色器对象判定:同时满足「色值是对象字面量 { ... } 的属性值」「该对象每个属性值都是色值」「对象内无任何非色值属性(数字、布尔、文案、嵌套对象、函数、var() 等)」三条时,才整体跳过。含非色值属性(如 { title: '主色', primary: '#0564f5' })则不算取色器,其中色值仍按常规规则判定。

blue 选择器向上回溯blue 保护常是「选择器在上、色值在下」的多行/嵌套结构。对每个色值匹配行须先向上回溯其所属 CSS 规则块的选择器(SCSS/Less 嵌套需逐层向上拼接所有祖先选择器,含 &.blue&-blue& 拼接),拼接后任一 class 名含 blue(大小写不敏感)→ 跳过。例:.card { .blue-tag { background: #4e80f5; } }#4e80f5 因祖先 .blue-tag 命中而跳过。

要求 5:rgba 色值使用 -rgb 伴侣变量替换

当硬编码色值为 rgba() 格式时,必须使用对应的 -rgb 伴侣变量并保留原有透明度:

rgba(5, 100, 245, 0.2)  → rgba(var(--brand-color-rgb), 0.2)
rgba(78, 128, 245, 0.15) → rgba(var(--brand-color-hover-rgb), 0.15)

核心原则:保留原始透明度,仅替换颜色分量部分。

注意:仅处理 rgba() 格式,rgb() 格式的色值按常规 hex 色值方式替换为 var(--brand-color) 等 hex 主变量。

8 位十六进制色值(含透明度)处理:源码中的 8 位色值 #RRGGBBAA前 6 位 #RRGGBB(大小写不敏感)与 2.1 表比对;命中则替换为 rgba(var(--xxx-rgb), <alpha>),其中 <alpha> = round(后 2 位 hex / 255, 2);未命中保留原样。

#0564f5ff  → var(--brand-color)                     // AA=FF 完全不透明,直接用主变量
#0564f533  → rgba(var(--brand-color-rgb), 0.2)       // 0x33/255 ≈ 0.2
#4e80f580  → rgba(var(--brand-color-hover-rgb), 0.5) // 0x80/255 ≈ 0.5
#123456ff  → 保留原样                                  // 前 6 位未命中

要求 6:先判定后替换,替换后执行遗漏检测

先判定后替换:每个色值匹配行在替换前,必须按固定顺序(不可打乱、不可跳步)跑完以下 6 项跳过检查,全部未命中才允许替换:

  1. 类名 / 变量名含色值(如 text-[#0564f5])→ 跳过
  2. 测试文件(*.test.**.spec.*__tests__/)→ 跳过
  3. 已注释行(以 ///* 开头、或被 /* */ 包裹)→ 跳过
  4. blue 类选择器回溯(向上回溯所属规则块,任一层祖先选择器含 blue)→ 跳过
  5. ECharts 属性 / 颜色数组 / var() 回退值 / 取色器对象 → 跳过
  6. SASS / Less 变量名含 blue$blue-base@blue-6)→ 跳过

块内已存在的 // var(...) 注释仅跳过其自身(检查 3),不构成对同块其他活跃行的替换许可——每一行都独立走完上述 6 项检查。

替换后执行遗漏检测(必须,可自动重试):本步骤第一轮替换完成后,须重新全文搜索所有待替换色值(含 6 位 hex、8 位 hex、rgb、rgba 的各格式变形),对命中行逐一判断是否属于上述合法跳过场景;若存在既不属于合法跳过、又未被替换的色值 → 判定为遗漏,立即重新替换,再次检测,直到无遗漏方可进入下一步骤。

bash
# 遗漏检测:搜索所有待替换的 6 位 hex 色值(排除已注释行、var() 回退值、测试文件、node_modules)
grep -rnE "#0564f5|#096dd9|#2065cf|#1d77ff|#1677ff|#0052d9|#2b7fff|#0060df|#1a7aff|#4e80f5|#1890ff|#40a9ff|#3886fb|#238dff|#3783ff|#4096ff|#5b8ff9|#69c0ff|#4ca1dc" \
  --include="*.scss" --include="*.css" --include="*.less" \
  --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" \
  --exclude-dir="__tests__" --exclude-dir="node_modules" -i \
  | grep -v "^\s*//" | grep -v "^\s*/\*" | grep -v "var("

要求 7:每次替换后检查代码逻辑

每次替换后,需确认改动不会破坏原有代码逻辑:

  • 确认 CSS 变量名拼写正确
  • 确认 var() 语法正确
  • 确认替换后颜色语义一致(主色 → --brand-color,hover 色 → --brand-color-hover
  • 若原代码依赖特定色值进行计算(如 darken()lighten() 等),调整为使用 CSS 变量或相应工具函数

要求 8:记录替换明细,汇入最终升级报告

本步骤替换过程中先临时记录改动明细(文件、原写法、替换后、匹配类型)。这些明细将在三大步骤全部完成后,依据当前项目代码的实际改动git diff / git status)统一核对,汇总进项目根目录的 Neo主题换肤功能升级报告.md(一份报告覆盖硬编码替换、旧变量迁移、按钮改造三部分)。报告数据以实际 diff 为准,临时记录若与 diff 不符则以 diff 为准修正。

本步骤(硬编码色值替换)相关的报告片段格式如下:

markdown
# 硬编码色值 CSS 变量替换

## 汇总信息

| 指标 | 数值 |
|:--|:--|
| 扫描文件总数 | XXX |
| 修改文件总数 | XXX |
| 替换次数(主品牌色·完全匹配) | XXX |
| 替换次数(次品牌色·完全匹配) | XXX |
| 替换次数(近似匹配·1 色阶) | XXX |
| 替换次数(近似匹配·2 色阶) | XXX |
| 跳过次数 | XXX |
| 操作日期 | YYYY-MM-DD |

## 跳过详情

| 文件 | 行号 | 色值 | 跳过原因 |
|:--|:--|:--|:--|
| src/components/Foo.scss | 12 | #0564f5 | 变量名保护:类名含色值 text-[#0564f5] |
| src/components/Foo.scss | 78 | #3080d0 | 超阈值:与基准 #0564f5 差 Δ_ch=43,不视为品牌色 |
| ... | ... | ... | ... |

## 完全匹配替换详情

| 文件 | 原写法 | 替换后 | 状态 |
|:--|:--|:--|:--|
| src/components/Header.scss | `color: #0564f5` | `color: var(--brand-color)` | ✅ |
| src/components/Button.scss | `background: #4e80f5` | `background: var(--brand-color-hover)` | ✅ |
| ... | ... | ... | ... |

## 近似匹配详情(需人工复核)

> 匹配基准恒为 `#0564f5`,替换目标恒为 `var(--brand-color)`(含透明度时用 `var(--brand-color-rgb)`)。

| 文件 | 行号 | 源色值 | 匹配基准 | 色阶差 | Δ_ch | d | 替换为 |
|:--|:--|:--|:--|:--|:--|:--|:--|
| src/Foo.scss | 12 | `#0968f5` | `#0564f5` | 1 色阶 | 4 | 5.7 | `var(--brand-color)` |
| ... | ... | ... | ... | ... | ... | ... | ... |

要求 9:不修改非相关内容

  • 仅替换匹配硬编码色值列表中的值
  • 不修改未在替换规则中列出的颜色值(如 #ff0000#00ff00 等)
  • 不修改代码结构、逻辑或其它样式属性

2.3 替换前后示例

SCSS 文件示例

scss
// ========== 替换前 ==========
.nav-menu {
  background-color: #0564f5;

  &:hover {
    background-color: #4e80f5;
  }
}

.menu-item.active {
  border-left: 3px solid #096dd9;
  color: #1890ff;
}

.stat-card {
  background: rgb(5, 100, 245);
}

// ========== 替换后 ==========
.nav-menu {
  // background-color: #0564f5;  // [*] 已替换为 var(--brand-color)
  background-color: var(--brand-color);

  &:hover {
    // background-color: #4e80f5;  // [*] 已替换为 var(--brand-color-hover)
    background-color: var(--brand-color-hover);
  }
}

.menu-item.active {
  // border-left: 3px solid #096dd9;  // [*] 已替换为 var(--brand-color)
  border-left: 3px solid var(--brand-color);
  // color: #1890ff;  // [*] 已替换为 var(--brand-color-hover)
  color: var(--brand-color-hover);
}

.stat-card {
  // background: rgb(5, 100, 245);  // [*] 已替换为 var(--brand-color)
  background: var(--brand-color);
}

CSS 文件示例

css
/* ========== 替换前 ========== */
.btn-primary {
  background-color: #0564f5;
  border-color: #4e80f5;
}

/* ========== 替换后 ========== */
.btn-primary {
  /* background-color: #0564f5; */ /* [*] 已替换为 var(--brand-color) */
  background-color: var(--brand-color);
  /* border-color: #4e80f5; */ /* [*] 已替换为 var(--brand-color-hover) */
  border-color: var(--brand-color-hover);
}

TSX 文件示例

tsx
// ========== 替换前 ==========
const styles = {
  headerBg: '#0564f5',
  linkColor: '#4e80f5',
};

<div style={{ backgroundColor: '#0564f5', color: '#1890ff' }}>

// ========== 替换后 ==========
const styles = {
  headerBg: 'var(--brand-color)',
  linkColor: 'var(--brand-color-hover)',
};

<div style={{ backgroundColor: 'var(--brand-color)', color: 'var(--brand-color-hover)' }}>

三、步骤二:旧 CSS 变量迁移

遍历所有样式文件,将旧的按钮色 CSS 变量替换为新的 CSS 变量写法。

说明:旧变量名带有 -bg 后缀,新变量名去掉了 -bg 后缀且统一了命名规范。

3.1 新旧变量对照表

旧 CSS 变量(查找)新 CSS 变量(替换为)
--color-button-filled-primary-bg--color-button-filled-primary
--color-button-filled-primary-bg-hover--color-button-filled-primary-hover
--color-button-filled-primary-bg-tint--color-button-filled-primary-tint
--color-button-filled-primary-bg-disabled--color-button-filled-primary-disabled
--color-button-filled-secondary-bg--color-button-filled-primary-secondary
--color-button-filled-secondary-bg-hover--color-button-filled-primary-secondary-hover
--color-button-filled-secondary-bg-disabled--color-button-filled-primary-secondary-disabled

3.2 操作方式

在项目源码中全文搜索旧变量名,逐项替换为新变量名:

bash
# 搜索旧变量
grep -r "filled-primary-bg" src/
grep -r "filled-secondary-bg" src/

# 确认命中的文件和行号后,逐一替换

3.3 替换前后示例

scss
// ========== 替换前 ==========
.btn-primary {
  background: var(--color-button-filled-primary-bg);

  &:hover {
    background: var(--color-button-filled-primary-bg-hover);
  }

  &:disabled {
    background: var(--color-button-filled-primary-bg-disabled);
  }
}

.btn-secondary {
  background: var(--color-button-filled-secondary-bg);

  &:hover {
    background: var(--color-button-filled-secondary-bg-hover);
  }

  &:disabled {
    background: var(--color-button-filled-secondary-bg-disabled);
  }
}

// ========== 替换后 ==========
.btn-primary {
  background: var(--color-button-filled-primary);

  &:hover {
    background: var(--color-button-filled-primary-hover);
  }

  &:disabled {
    background: var(--color-button-filled-primary-disabled);
  }
}

.btn-secondary {
  background: var(--color-button-filled-primary-secondary);

  &:hover {
    background: var(--color-button-filled-primary-secondary-hover);
  }

  &:disabled {
    background: var(--color-button-filled-primary-secondary-disabled);
  }
}

四、步骤三:按钮色改造

遍历所有样式文件,对 primary 模式按钮应用按钮色 CSS 变量。

重要:需要遍历完同一文件内的所有 primary 按钮样式块,不可遗漏。

4.1 使用规则 1:白色背景 primary 按钮

适用条件:class 含 button-primary / ant-btn-primary / btn-primary / a-Button--primary,且原有背景色为白色(#FFFFFF / #FFF / #ffffff / #fff)。

状态操作
非 hover / activecolorborder-color 改用 var(--color-button-filled-primary)
hover 态colorborder-color 改用 var(--color-button-filled-primary-hover)
active 态colorborder-color 改用 var(--color-button-filled-primary-active)
scss
// ========== 替换前:白色背景 primary 按钮 ==========
.btn-primary {
  background: #fff;
  color: #0564f5;
  border-color: #0564f5;

  &:hover {
    background: #f0f7ff;
    color: #4e80f5;
    border-color: #4e80f5;
  }

  &:active {
    color: #096dd9;
    border-color: #096dd9;
  }
}

// ========== 替换后 ==========
.btn-primary {
  background: #fff;
  color: var(--color-button-filled-primary);
  border-color: var(--color-button-filled-primary);

  &:hover {
    background: #f0f7ff;
    color: var(--color-button-filled-primary-hover);
    border-color: var(--color-button-filled-primary-hover);
  }

  &:active {
    color: var(--color-button-filled-primary-active);
    border-color: var(--color-button-filled-primary-active);
  }
}

4.2 使用规则 2:非白色背景 primary 按钮

适用条件:class 含 button-primary / ant-btn-primary / btn-primary / a-Button--primary,且原有背景色不是白色。

状态操作
非 hover / activebackgroundbackground-color 改用 var(--color-button-filled-primary)color 改用 var(--color-button-filled-primary-color)
hover 态backgroundbackground-color 改用 var(--color-button-filled-primary-hover)color 改用 var(--color-button-filled-primary-color-hover)
active 态backgroundbackground-color 改用 var(--color-button-filled-primary-active)color 改用 var(--color-button-filled-primary-color-hover)

💡 为什么字体色 color 必须使用按钮字体色变量?

填充型 primary 按钮的文字直接叠在按钮背景色之上。如果 color 使用固定色值(如 #fff)或固定色值的 CSS 变量,一旦运行时把按钮背景色换成与该文字色相近的颜色,就会出现文字与背景色相近、看起来模糊的问题。

--color-button-filled-primary-color / --color-button-filled-primary-color-hover 是随按钮背景色自动计算的对比色,能保证任意按钮色下文字都清晰可读。因此非白色背景 primary 按钮的 color 一律改用按钮字体色变量:

  • 非 hover / active 态 → var(--color-button-filled-primary-color)
  • hover / active 态 → var(--color-button-filled-primary-color-hover)

白色背景轮廓 primary 按钮的 color 显示的是品牌色本身(文字即品牌色),仍使用 --color-button-filled-primary 系列,不改用按钮字体色变量。

scss
// ========== 替换前:非白色背景 primary 按钮 ==========
.ant-btn-primary {
  background-color: #0564f5;
  color: #fff;
  border: none;

  &:hover {
    background-color: #4e80f5;
  }

  &:active {
    background-color: #096dd9;
  }
}

// ========== 替换后 ==========
.ant-btn-primary {
  background-color: var(--color-button-filled-primary);
  color: var(--color-button-filled-primary-color);
  border: none;

  &:hover {
    background-color: var(--color-button-filled-primary-hover);
    color: var(--color-button-filled-primary-color-hover);
  }

  &:active {
    background-color: var(--color-button-filled-primary-active);
  }
}

4.3 使用规则 3:所有 primary 模式按钮通用规则

遍历所有按钮类组件元素,找出 primary 模式的按钮,按以下规则实现:

条件状态操作
背景白色非 hover / activecolor + border-colorvar(--color-button-filled-primary)
背景白色hover 态color + border-colorvar(--color-button-filled-primary-hover)
背景白色active 态color + border-colorvar(--color-button-filled-primary-active)
背景非白色非 hover / activebackground-colorvar(--color-button-filled-primary)colorvar(--color-button-filled-primary-color)
背景非白色hover 态background-colorvar(--color-button-filled-primary-hover)colorvar(--color-button-filled-primary-color-hover)
背景非白色active 态background / background-colorvar(--color-button-filled-primary-active)colorvar(--color-button-filled-primary-color-hover)

💡 字体色规则说明:非白色背景 primary 按钮的文字色 color 必须使用按钮字体色变量(非 hover/active 态 → --color-button-filled-primary-color;hover/active 态 → --color-button-filled-primary-color-hover),而非固定色值或固定色值 CSS 变量,避免文字色与运行时切换后的按钮背景色相近导致文字模糊。

4.4 可用的按钮色 CSS 变量

CSS 变量-rgb 伴侣变量(默认值)说明
--color-button-filled-primary5, 100, 245填充主按钮 — 正常态背景色(★ 同时可用于 border/outline/text 颜色)
--color-button-filled-primary-rgbrgba() 半透明场景使用
--color-button-filled-primary-hover26, 122, 255填充主按钮 — 鼠标悬停背景色
--color-button-filled-primary-hover-rgbrgba() 半透明场景使用
--color-button-filled-primary-active0, 76, 207填充主按钮 — 点击按下背景色
--color-button-filled-primary-active-rgbrgba() 半透明场景使用
--color-button-filled-primary-tint230, 244, 255填充主按钮 — 浅色背景
--color-button-filled-primary-tint-rgbrgba() 半透明场景使用
--color-button-filled-primary-disabled128, 191, 255填充主按钮 — 禁用态背景色
--color-button-filled-primary-disabled-rgbrgba() 半透明场景使用
--color-button-filled-primary-color填充主按钮 — 正常态文字色(自动对比色)
--color-button-filled-primary-color-hover填充主按钮 — 悬停文字色
--color-button-filled-primary-color-disabled填充主按钮 — 禁用文字色
--color-button-filled-primary-secondary26, 122, 255★ 次按钮 — 正常态背景(降一级变浅,colors[4])
--color-button-filled-primary-secondary-rgbrgba() 半透明场景使用
--color-button-filled-primary-secondary-hover5, 100, 245★ 次按钮 — 悬停态背景(比 hover 降一级变浅)
--color-button-filled-primary-secondary-hover-rgbrgba() 半透明场景使用
--color-button-filled-primary-secondary-disabled168, 215, 255★ 次按钮 — 禁用态背景(比 disabled 降一级变浅)
--color-button-filled-primary-secondary-disabled-rgbrgba() 半透明场景使用

4.5 按钮色改造前后示例

场景 A:白色背景的 primary 轮廓按钮

scss
// ========== 改造前 ==========
.button-primary {
  background-color: #fff;
  color: #0564f5;
  border: 1px solid #0564f5;
  border-radius: 6px;
  padding: 8px 20px;
  cursor: pointer;

  &:hover {
    color: #4e80f5;
    border-color: #4e80f5;
  }

  &:active {
    color: #096dd9;
    border-color: #096dd9;
  }

  &:disabled {
    color: #80bfff;
    border-color: #80bfff;
    cursor: not-allowed;
  }
}

// ========== 改造后 ==========
.button-primary {
  background-color: #fff;
  color: var(--color-button-filled-primary);
  border: 1px solid var(--color-button-filled-primary);
  border-radius: 6px;
  padding: 8px 20px;
  cursor: pointer;

  &:hover {
    color: var(--color-button-filled-primary-hover);
    border-color: var(--color-button-filled-primary-hover);
    background-color: rgba(var(--color-button-filled-primary-rgb), 0.06);
  }

  &:active {
    color: var(--color-button-filled-primary-active);
    border-color: var(--color-button-filled-primary-active);
  }

  &:disabled {
    color: var(--color-button-filled-primary-disabled);
    border-color: var(--color-button-filled-primary-disabled);
    cursor: not-allowed;
  }
}

场景 B:非白色背景的填充 primary 按钮

scss
// ========== 改造前 ==========
.ant-btn-primary {
  background: #0564f5;
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 8px 24px;
  font-weight: 500;
  cursor: pointer;

  &:hover {
    background: #4e80f5;
  }

  &:active {
    background: #096dd9;
  }

  &:disabled {
    background: #80bfff;
    cursor: not-allowed;
  }
}

// ========== 改造后 ==========
.ant-btn-primary {
  background: var(--color-button-filled-primary);
  color: var(--color-button-filled-primary-color);
  border: none;
  border-radius: 6px;
  padding: 8px 24px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.3s ease;

  &:hover {
    background: var(--color-button-filled-primary-hover);
    color: var(--color-button-filled-primary-color-hover);
  }

  &:active {
    background: var(--color-button-filled-primary-active);
    color: var(--color-button-filled-primary-color-hover);
  }

  &:disabled {
    background: var(--color-button-filled-primary-disabled);
    color: var(--color-button-filled-primary-color-disabled);
    cursor: not-allowed;
  }
}

场景 C:次按钮

scss
// ========== 改造前 ==========
.btn-secondary {
  background: #a8d7ff;
  color: #fff;
  border: none;
  border-radius: 6px;
  padding: 6px 18px;
  cursor: pointer;

  &:hover {
    background: #80bfff;
  }

  &:disabled {
    background: #e6f4ff;
    cursor: not-allowed;
  }
}

// ========== 改造后 ==========
.btn-secondary {
  background: var(--color-button-filled-primary-secondary);
  color: var(--color-button-filled-primary-color);
  border: none;
  border-radius: 6px;
  padding: 6px 18px;
  cursor: pointer;

  &:hover {
    background: var(--color-button-filled-primary-secondary-hover);
  }

  &:disabled {
    background: var(--color-button-filled-primary-secondary-disabled);
    color: var(--color-button-filled-primary-color-disabled);
    cursor: not-allowed;
  }
}

五、升级完成检查清单

完成以上三个步骤后,请逐项检查:

硬编码色值替换

  • [ ] 匹配总则已遵守(完全匹配优先;1-2 色阶近似匹配仅以 #0564f5 为基准并标记 [近似匹配];超阈值一律保留;不基于色名扩展)
  • [ ] 所有匹配的硬编码色值已替换为 var(--brand-color) / var(--brand-color-hover)(含 6 位 hex、8 位 hex、rgb、rgba 格式)
  • [ ] 8 位 hex 色值(#RRGGBBAA)已按前 6 位命中 + alpha 换算规则处理
  • [ ] 类名/变量名中含色值的情况已正确跳过
  • [ ] SCSS 文件中已用 // 注释保留原有写法
  • [ ] CSS 文件中已用 /* */ 注释保留原有写法
  • [ ] 测试文件均已跳过
  • [ ] ECharts option 中的色值未替换
  • [ ] 颜色数组中的色值未替换
  • [ ] 取色器 / 调色板对象(属性值全为色值)中的色值未替换
  • [ ] var() 默认值中的色值未替换
  • [ ] class 含 blue 关键字、以及祖先选择器回溯后含 blue 的未替换
  • [ ] 已执行遗漏检测,仅剩合法跳过项,无遗漏
  • [ ] 改动明细已记录,待汇入最终 Neo主题换肤功能升级报告.md
  • [ ] 无破坏代码逻辑的变更

旧 CSS 变量迁移

  • [ ] --color-button-filled-primary-bg*--color-button-filled-primary* 全部替换
  • [ ] --color-button-filled-secondary-bg*--color-button-filled-primary-secondary* 全部替换

按钮色改造

  • [ ] 白色背景 primary 按钮的 color + border-color 已使用按钮色变量
  • [ ] 非白色背景 primary 按钮的 background-color 已使用按钮色变量
  • [ ] 非白色背景 primary 按钮的字体色 color 已改用按钮字体色变量(非 hover/active 态 → --color-button-filled-primary-color;hover/active 态 → --color-button-filled-primary-color-hover
  • [ ] 每个文件内的所有 primary 按钮样式块均已遍历
  • [ ] hover / active / disabled 各状态均有对应变量
  • [ ] 次按钮已使用 --color-button-filled-primary-secondary* 变量
  • [ ] ant-btn-primarybackground-color 已确认替换
  • [ ] 已执行按钮改造遗漏检测,所有 primary 按钮的 background-color 均为 CSS 变量或白色背景

最终验证

  • [ ] 三大步骤全部完成后,已依据实际代码改动(git diff / git status)生成 Neo主题换肤功能升级报告.md
  • [ ] 报告内容与实际代码改动一致(改动文件数、替换次数、逐文件明细均与 git diff 核对无误)
  • [ ] 项目构建无报错(npm run build
  • [ ] 切换不同主题后页面颜色正常响应
  • [ ] 各按钮的 hover / active / disabled 状态交互正常
  • [ ] ECharts 图表颜色显示正常

手动调试验证:在浏览器控制台执行以下命令,切换品牌色和按钮色确认效果:

js
// 切换品牌色(例:紫色)
NeoThemeHelper.setBaseColor("#923dda")

// 切换按钮色(例:橙色)
NeoThemeHelper.setButtonPrimaryColor("#ff6b35")

提示主题换肤在线 Demo 提供交互式控制面板,可直观预览各主题效果。