diff --git a/ry-vue3/src/assets/theme.js b/ry-vue3/src/assets/theme.js index 64cbacb..3a59833 100644 --- a/ry-vue3/src/assets/theme.js +++ b/ry-vue3/src/assets/theme.js @@ -74,21 +74,6 @@ function hslToHex({ h, s, l }) { /** 把 lightness 限制在 8..92 之间 */ function clampL(l) { return Math.max(8, Math.min(92, l)) } -/** - * Element Plus mix() — RGB 线性插值 (Sass mixColor 复刻) - * mix(color1, color2, weight) → color1*(1-w) + color2*w - * 比 HSL 大幅提亮更克制, 适合政务网站的浅色调. - */ -function elMix(color1, color2, weight) { - weight = Math.max(Math.min(weight, 1), 0) - const r1 = parseInt(color1.slice(1, 3), 16), g1 = parseInt(color1.slice(3, 5), 16), b1 = parseInt(color1.slice(5, 7), 16) - const r2 = parseInt(color2.slice(1, 3), 16), g2 = parseInt(color2.slice(3, 5), 16), b2 = parseInt(color2.slice(5, 7), 16) - const r = Math.round(r1 * (1 - weight) + r2 * weight) - const g = Math.round(g1 * (1 - weight) + g2 * weight) - const b = Math.round(b1 * (1 - weight) + b2 * weight) - return '#' + [r, g, b].map(v => v.toString(16).padStart(2, '0')).join('') -} - /** * 通用派生算法:输入主色 hex, 输出 7 个 --brand--* 变量 * @param {string} base 主色 hex @@ -98,11 +83,13 @@ function elMix(color1, color2, weight) { * 深色 (HSL lightness): * deep = L - 12 (hover / active / 标题强调) * darker = L - 22 (顶部导航 / 深底) - * 浅色 (Element Plus mix(white, base, N*10%)): - * light = 50% mix (背景 / disabled) - * lighter = 70% mix (hover-bg / 选中态) - * text = 30% mix (链接默认) - * mix = 80% mix (输入框 focus border) + * 浅色 (HSL 降饱和 + 提亮度, 适合政务网站): + * text = S-40 L+15 (链接默认, 中蓝可读) + * lighter = S-65 L+50 (hover-bg / 选中态, 浅灰) + * light = S-65 L+40 (背景 / disabled, 灰中) + * mix = S-75 L+60 (输入框 focus border, 近白) + * 之前试过 Element mix(white), 但 S=100% 的主色提亮后跟主色 + * 对比度太低 (例 #4da2b5), 不适合做选中态背景. */ export function derivePalette(base, prefix = 'primary') { const hsl = rgbToHsl(hexToRgb(base)) @@ -111,10 +98,10 @@ export function derivePalette(base, prefix = 'primary') { [`--brand-${p}`]: base, [`--brand-${p}-deep`]: hslToHex({ ...hsl, l: clampL(hsl.l - 12) }), [`--brand-${p}-darker`]: hslToHex({ ...hsl, l: clampL(hsl.l - 22) }), - [`--brand-${p}-light`]: elMix('#ffffff', base, 0.5), - [`--brand-${p}-lighter`]: elMix('#ffffff', base, 0.7), - [`--brand-${p}-text`]: elMix('#ffffff', base, 0.3), - [`--brand-${p}-mix`]: elMix('#ffffff', base, 0.8) + [`--brand-${p}-text`]: hslToHex({ h: hsl.h, s: Math.max(0, hsl.s - 40), l: clampL(hsl.l + 15) }), + [`--brand-${p}-lighter`]: hslToHex({ h: hsl.h, s: Math.max(0, hsl.s - 65), l: clampL(hsl.l + 50) }), + [`--brand-${p}-light`]: hslToHex({ h: hsl.h, s: Math.max(0, hsl.s - 65), l: clampL(hsl.l + 40) }), + [`--brand-${p}-mix`]: hslToHex({ h: hsl.h, s: Math.max(0, hsl.s - 75), l: clampL(hsl.l + 60) }) } } diff --git a/ry-vue3/src/assets/theme.scss b/ry-vue3/src/assets/theme.scss index 3c28ad1..7b4f987 100644 --- a/ry-vue3/src/assets/theme.scss +++ b/ry-vue3/src/assets/theme.scss @@ -8,13 +8,16 @@ 深色 (deep/darker) — HSL 调亮度 --brand-primary-deep 暗 12% (hover / active / 标题强调) --brand-primary-darker 暗 22% (顶部导航 / 深底) - 浅色 (light/lighter/text/mix) — Element Plus mix(white, base, N*10%) - --brand-primary-light mix 50% white (背景 / disabled) - --brand-primary-lighter mix 70% white (hover-bg / 选中态) - --brand-primary-text mix 30% white (链接默认) - --brand-primary-mix mix 80% white (输入框 focus border) - 之前 HSL +35% 算法会生成荧光色 (例 #49deff), 政务网站不适用; - 改用 Element mix() 才是 RGB 线性插值, 全是低饱和度灰调. + 浅色 (light/lighter/text/mix) — HSL 降饱和 + 提亮度 + 之前试过 Element mix(white) 算法, 但 S=100% 的主色提亮后 + 跟主色对比度太低 (例 #4da2b5 / #4db892), 文字与背景难辨认, + 不适合做选中态/hover-bg. + 现在: 同时降饱和 (S-N) 和提亮度 (L+N), 色相不变, 但灰度提升, + 是 Element 在 S 低基色上的实际效果模拟, 适合政务网站. + --brand-primary-text S-40 L+15 (链接默认, 中蓝可读) + --brand-primary-lighter S-65 L+50 (hover-bg / 选中态, 浅灰蓝) + --brand-primary-light S-65 L+40 (背景 / disabled, 灰中蓝) + --brand-primary-mix S-75 L+60 (输入框 focus border, 近白) 次色 (绿色 #009963) 走同一套算法, 命名 -secondary. 语义上对应 Element Plus success (绿=成功/已批准/已完成), 自动 @@ -29,19 +32,19 @@ --brand-primary: #007A95; --brand-primary-deep: #004858; --brand-primary-darker: #002129; - --brand-primary-light: #80bdca; - --brand-primary-lighter: #4da2b5; - --brand-primary-text: #b3d7df; - --brand-primary-mix: #3395aa; + --brand-primary-text: #2d9cb4; + --brand-primary-lighter: #b7d6dd; + --brand-primary-light: #95c2cc; + --brand-primary-mix: #dde8ea; /* 次色 — 翠绿 */ --brand-secondary: #009963; --brand-secondary-deep: #005c3b; --brand-secondary-darker: #00291a; - --brand-secondary-light: #80ccb1; - --brand-secondary-lighter: #4db892; - --brand-secondary-text: #b3e0d0; - --brand-secondary-mix: #33ad82; + --brand-secondary-text: #2eb887; + --brand-secondary-lighter: #baded1; + --brand-secondary-light: #98cdba; + --brand-secondary-mix: #dfece7; } /* Element Plus 联动:把品牌色同步给 Element Plus 内置变量。 @@ -49,10 +52,11 @@ :root { /* 主色 → primary */ --el-color-primary: var(--brand-primary); - --el-color-primary-light-3: var(--brand-primary-text); /* text */ - --el-color-primary-light-5: var(--brand-primary-light); /* light */ - --el-color-primary-light-7: var(--brand-primary-lighter); /* lighter */ - --el-color-primary-light-9: var(--brand-primary-lighter); /* lighter */ + --el-color-primary-light-3: var(--brand-primary-text); /* text (中蓝可读) */ + --el-color-primary-light-5: var(--brand-primary-light); /* light (灰中) */ + --el-color-primary-light-7: var(--brand-primary-lighter); /* lighter (灰浅) */ + --el-color-primary-light-8: var(--brand-primary-lighter); /* lighter */ + --el-color-primary-light-9: var(--brand-primary-mix); /* mix (近白) */ --el-color-primary-dark-2: var(--brand-primary-deep); /* 次色 → success (绿色语义: 已批准 / 已完成) */ @@ -61,6 +65,6 @@ --el-color-success-light-5: var(--brand-secondary-light); --el-color-success-light-7: var(--brand-secondary-lighter); --el-color-success-light-8: var(--brand-secondary-lighter); - --el-color-success-light-9: var(--brand-secondary-lighter); + --el-color-success-light-9: var(--brand-secondary-mix); --el-color-success-dark-2: var(--brand-secondary-deep); } \ No newline at end of file