From 57d6e508849221d92fa8fd3534e269478017103d Mon Sep 17 00:00:00 2001 From: Syngnat Date: Sun, 26 Jul 2026 21:51:08 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20style(transaction):=20=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E4=BA=8B=E5=8A=A1=E6=8F=90=E4=BA=A4=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E6=94=B9=E7=94=A8=20warn=20=E7=8B=AC=E7=AB=8B=E9=85=8D?= =?UTF-8?q?=E8=89=B2=E5=B9=B6=E4=BF=AE=E5=A4=8D=E5=BE=85=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=9D=A1=E6=95=B0=E7=9C=8B=E4=B8=8D=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 手动事务的「提交」按钮不再跟随主题强调色:它是需要用户显式决断的状态,与普通主操作同色时 无法区分。改用 warn 实心填充,与已是 danger 的「回滚」形成语义配对。 原先是 accent-soft 底 + accent-2 文字,同色系淡色叠淡色,文字本身对比就很低 - 修复待提交条数徽标的数字看不见:底色原为硬编码 rgba(22,163,74,.18)、文字为 --gn-accent-2, 而按钮底是 --gn-accent —— 三者几乎同色,实测对比度仅 1.10–1.17, 静止状态数字完全不可见,只有 hover 把按钮压暗后才显现(DataGrid 与 SQL 编辑器两处同病) - 新增 --gn-on-warn / --gn-warn-hover / --gn-warn-active / --gn-warn-badge-bg / --gn-accent-badge-bg。前景色按亮度择优,因为 warn 跨度很大(#9f5f1d ~ #ebcb8b); 徽标底色的叠加方向也随前景色反向偏移,写死半透明白会让浅色主题的白字只剩 2.65 - 补 3 项不变式测试:warn 标签与徽标数字对比度均 ≥4.5、warn 三态逐级加深且互不相同。 该用例已实测抓住浅色主题 warm-paper 的徽标对比不足 实测:徽标数字对比度由 1.10–1.17 提升到 ≥4.5;warn 标签 7.59–11.80。 --- frontend/src/styles/v2-theme-workbench.css | 38 +++++++---- frontend/src/utils/customThemePresets.ts | 63 +++++++++++++++++++ .../customThemePresetsAccentStates.test.ts | 45 +++++++++++++ frontend/src/v2-theme.css | 15 ++++- 4 files changed, 146 insertions(+), 15 deletions(-) diff --git a/frontend/src/styles/v2-theme-workbench.css b/frontend/src/styles/v2-theme-workbench.css index 65314d97..72fe065d 100644 --- a/frontend/src/styles/v2-theme-workbench.css +++ b/frontend/src/styles/v2-theme-workbench.css @@ -262,38 +262,50 @@ body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button { display: inline-flex !important; align-items: center; gap: 6px; - border-color: transparent !important; - background: var(--gn-accent-soft) !important; - color: var(--gn-accent-2) !important; + /* 待提交事务用 warn 实心填充,刻意不跟随主题强调色: + 它是需要用户显式决断的状态,与普通主操作同色时无法区分(回滚用 danger,二者语义配对)。 + 原先是 accent-soft 底 + accent-2 文字,两者都是同一色系的淡色,实测文字对比极低。 */ + border-color: var(--gn-warn) !important; + background: var(--gn-warn) !important; + color: var(--gn-on-warn, #17130a) !important; font-weight: 750 !important; transition: background-color 120ms ease, box-shadow 120ms ease, border-color 120ms ease !important; } body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:hover, body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:focus, -body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:focus-visible, -body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:active { - border-color: rgba(22, 163, 74, 0.18) !important; - background: color-mix(in srgb, var(--gn-accent-soft) 80%, rgba(22, 163, 74, 0.28)) !important; - color: var(--gn-accent-2) !important; - box-shadow: 0 0 0 1px rgba(22, 163, 74, 0.12), 0 6px 14px rgba(22, 163, 74, 0.12) !important; +body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:focus-visible { + border-color: var(--gn-warn-hover, var(--gn-warn)) !important; + background: var(--gn-warn-hover, var(--gn-warn)) !important; + color: var(--gn-on-warn, #17130a) !important; + box-shadow: var(--gn-shadow-sm), inset 0 0.5px 0 rgba(255, 255, 255, 0.15) !important; } +body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:active { + border-color: var(--gn-warn-active, var(--gn-warn)) !important; + background: var(--gn-warn-active, var(--gn-warn)) !important; + color: var(--gn-on-warn, #17130a) !important; + box-shadow: none !important; +} + +/* 待提交条数徽标:底色用白色半透明叠加而非硬编码绿色,文字用 on-warn。 + 原先底是 rgba(22,163,74,.18)、文字是 --gn-accent-2,与按钮底色几乎同色 + (实测对比度仅 1.10–1.17),静止状态下数字完全看不见,只有 hover 变暗后才显现。 */ body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button .gn-v2-toolbar-kbd { margin-left: 0; min-width: 18px; justify-content: center; border: none; - background: rgba(22, 163, 74, 0.18); - color: var(--gn-accent-2); + background: var(--gn-warn-badge-bg, rgba(255, 255, 255, 0.35)); + color: var(--gn-on-warn, #17130a); } body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:hover .gn-v2-toolbar-kbd, body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:focus .gn-v2-toolbar-kbd, body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:focus-visible .gn-v2-toolbar-kbd, body[data-ui-version="v2"] .gn-v2-query-transaction-commit-button:active .gn-v2-toolbar-kbd { - background: rgba(22, 163, 74, 0.24); - color: var(--gn-accent-2); + background: var(--gn-warn-badge-bg, rgba(255, 255, 255, 0.42)); + color: var(--gn-on-warn, #17130a); } body[data-ui-version="v2"] .gn-v2-query-toolbar-icon-action.ant-btn, diff --git a/frontend/src/utils/customThemePresets.ts b/frontend/src/utils/customThemePresets.ts index 9ac4335a..e53cd461 100644 --- a/frontend/src/utils/customThemePresets.ts +++ b/frontend/src/utils/customThemePresets.ts @@ -105,6 +105,60 @@ const parseHexColor = (value: string): [number, number, number] | null => { ]; }; +const channelLuminance = (channel: number): number => { + const c = channel / 255; + return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4; +}; + +/** + * readableOnColor 按底色亮度挑选可读的前景色。 + * + * warn 在各预设间跨度很大(浅色主题为 #9f5f1d 这类深琥珀,深色主题为 #ebcb8b 这类浅琥珀), + * 固定用深色或白色都会在另一端失效,因此按相对亮度择优。 + */ +const DARK_ON_COLOR = '#17130a'; + +const readableOnColor = (background: string): string => { + const rgb = parseHexColor(background); + if (!rgb) { + return '#ffffff'; + } + const relativeLuminance = (channels: [number, number, number]): number => ( + 0.2126 * channelLuminance(channels[0]) + + 0.7152 * channelLuminance(channels[1]) + + 0.0722 * channelLuminance(channels[2]) + ); + const contrast = (a: number, b: number): number => { + const [hi, lo] = a >= b ? [a, b] : [b, a]; + return (hi + 0.05) / (lo + 0.05); + }; + + const backgroundLuminance = relativeLuminance(rgb); + const darkRgb = parseHexColor(DARK_ON_COLOR); + const darkContrast = darkRgb + ? contrast(backgroundLuminance, relativeLuminance(darkRgb)) + : 1; + const whiteContrast = contrast(backgroundLuminance, 1); + return darkContrast >= whiteContrast ? DARK_ON_COLOR : '#ffffff'; +}; + +/** + * badgeBackground 计算「按钮内计数徽标」的底色。 + * + * 叠加方向必须跟随前景色,不能写死成半透明白: + * warn 在浅色主题下是深琥珀(#9f5f1d),前景色随之取白,此时若把徽标底往白色叠加, + * 白字只剩 2.65 的对比度;反之深色主题的浅琥珀底配深字,则必须往白色叠加。 + * 因此按前景色是深还是浅,决定徽标底往相反方向偏移。 + */ +const badgeBackground = (base: string, onColor: string): string => { + const rgb = parseHexColor(onColor); + // 前景偏深时把徽标底往白色偏移,前景偏浅(白字)时往黑色偏移。 + const onIsDark = rgb + ? 0.2126 * channelLuminance(rgb[0]) + 0.7152 * channelLuminance(rgb[1]) + 0.0722 * channelLuminance(rgb[2]) < 0.5 + : true; + return mixHex(base, onIsDark ? '#ffffff' : '#000000', 0.65); +}; + /** mixHex 按 ratio 保留 accent、其余混入 towards,返回十六进制。 */ const mixHex = (accent: string, towards: string, ratio: number): string => { const a = parseHexColor(accent); @@ -177,6 +231,15 @@ body[data-custom-theme][data-ui-version="v2"] { --gn-accent-hover: ${mixHex(palette.accent, accentStateAnchor(palette), ACCENT_HOVER_RATIO)}; --gn-accent-active: ${mixHex(palette.accent, accentStateAnchor(palette), ACCENT_ACTIVE_RATIO)}; + /* 手动事务的「提交」按钮刻意不跟随主题强调色:待提交事务是需要用户显式决断的状态, + 若与普通主操作同色则无法区分(回滚已用 danger,提交用 warn 形成语义配对)。 + on-warn 按亮度择优,因为 warn 在各预设间跨度很大(#9f5f1d ~ #ebcb8b)。 */ + --gn-on-warn: ${readableOnColor(palette.warn)}; + --gn-warn-hover: ${mixHex(palette.warn, accentStateAnchor(palette), ACCENT_HOVER_RATIO)}; + --gn-warn-active: ${mixHex(palette.warn, accentStateAnchor(palette), ACCENT_ACTIVE_RATIO)}; + --gn-warn-badge-bg: ${badgeBackground(palette.warn, readableOnColor(palette.warn))}; + --gn-accent-badge-bg: ${badgeBackground(palette.accent, palette.onAccent)}; + --gn-ant-primary: ${palette.accent}; --gn-ant-primary-hover: ${mixHex(palette.accent, accentStateAnchor(palette), ACCENT_HOVER_RATIO)}; --gn-ant-primary-active: ${mixHex(palette.accent, accentStateAnchor(palette), ACCENT_ACTIVE_RATIO)}; diff --git a/frontend/src/utils/customThemePresetsAccentStates.test.ts b/frontend/src/utils/customThemePresetsAccentStates.test.ts index 3cf81316..e06c2c58 100644 --- a/frontend/src/utils/customThemePresetsAccentStates.test.ts +++ b/frontend/src/utils/customThemePresetsAccentStates.test.ts @@ -102,3 +102,48 @@ describe('内置主题的主要操作按钮三态', () => { } }); }); + +/** + * 手动事务「提交」按钮用 warn 实心填充,刻意不跟随主题强调色。 + * 回归背景:原先用 accent-soft 底 + accent-2 文字,与普通主操作同色系且文字对比极低; + * 待提交条数徽标更是用硬编码绿底 + accent-2 文字,实测对比度仅 1.10–1.17, + * 静止状态下数字完全看不见,只有 hover 把按钮压暗后才显现。 + */ +describe('手动事务提交按钮的 warn 配色', () => { + it('warn 前景色在每个主题下都达到正文可读级(≥4.5)', () => { + for (const preset of BUILTIN_CUSTOM_THEME_PRESETS) { + const warn = readToken(preset.css, '--gn-warn'); + const onWarn = readToken(preset.css, '--gn-on-warn'); + expect( + contrastRatio(onWarn, warn), + `${preset.id} 的 warn 标签对比度不足`, + ).toBeGreaterThanOrEqual(4.5); + } + }); + + it('待提交条数徽标的数字在静止状态即可读(≥4.5)', () => { + for (const preset of BUILTIN_CUSTOM_THEME_PRESETS) { + const onWarn = readToken(preset.css, '--gn-on-warn'); + const badgeHex = readToken(preset.css, '--gn-warn-badge-bg'); + expect( + contrastRatio(onWarn, badgeHex), + `${preset.id} 的徽标数字对比度不足`, + ).toBeGreaterThanOrEqual(4.5); + } + }); + + it('warn 的三态同样逐级加深且互不相同', () => { + for (const preset of BUILTIN_CUSTOM_THEME_PRESETS) { + const warn = readToken(preset.css, '--gn-warn'); + const hover = readToken(preset.css, '--gn-warn-hover'); + const active = readToken(preset.css, '--gn-warn-active'); + + expect(hover, `${preset.id} 的 warn hover 不应等于基色`).not.toBe(warn); + expect(active, `${preset.id} 的 warn active 不应等于 hover`).not.toBe(hover); + expect( + contrastRatio(warn, hover), + `${preset.id} 的 warn base→hover 对比度不足`, + ).toBeGreaterThanOrEqual(MIN_STATE_DELTA); + } + }); +}); diff --git a/frontend/src/v2-theme.css b/frontend/src/v2-theme.css index cde248fb..dd759709 100644 --- a/frontend/src/v2-theme.css +++ b/frontend/src/v2-theme.css @@ -4091,10 +4091,21 @@ body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-commit-button:disabled { box-shadow: none !important; } +/* 待提交条数徽标:底色改用白色半透明叠加、文字改用 on-accent。 + 原先底是硬编码的 rgba(22,163,74,.18)、文字是 --gn-accent-2, + 而按钮底色是 --gn-accent —— 三者几乎同色(实测对比度仅 1.10–1.17), + 静止状态下数字完全看不见,只有 hover 把按钮压暗后才显现。 */ body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-commit-button .gn-v2-toolbar-kbd { border: none; - background: rgba(22, 163, 74, 0.18); - color: var(--gn-accent-2); + background: var(--gn-accent-badge-bg, rgba(255, 255, 255, 0.35)); + color: var(--gn-on-accent, #17130a); +} + +body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-commit-button:hover .gn-v2-toolbar-kbd, +body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-commit-button:focus-visible .gn-v2-toolbar-kbd, +body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-commit-button:active .gn-v2-toolbar-kbd { + background: var(--gn-accent-badge-bg, rgba(255, 255, 255, 0.42)); + color: var(--gn-on-accent, #17130a); } body[data-ui-version="v2"] .gn-v2-data-grid .gn-v2-ai-insight-button {