fix(web): 皮肤主题 - 修复提示浮层文字对比度

This commit is contained in:
Ray
2026-09-07 15:00:37 +08:00
parent bddf6fc20b
commit 33d6ac341b
3 changed files with 64 additions and 0 deletions

View File

@@ -3,12 +3,14 @@ package service
import (
"bytes"
"encoding/json"
"math"
"mime/multipart"
"net/http"
"net/http/httptest"
"net/textproto"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
@@ -48,6 +50,52 @@ func TestAppearanceDefaultsPreserveBuiltInBrand(t *testing.T) {
}
}
func TestBuiltInAppearanceSkinTooltipPairsMeetContrast(t *testing.T) {
for _, skin := range defaultAppearanceSkinThemes() {
modes := []struct {
name string
tokens AppearanceSkinModeTokens
}{
{name: "light", tokens: skin.Tokens.Light},
{name: "dark", tokens: skin.Tokens.Dark},
}
for _, mode := range modes {
ratio := appearanceColorContrastRatio(t, mode.tokens.Overlay, mode.tokens.Text)
if ratio < 4.5 {
t.Errorf("skin %s %s tooltip contrast = %.2f, want at least 4.5", skin.ID, mode.name, ratio)
}
}
}
}
func appearanceColorContrastRatio(t *testing.T, foreground, background string) float64 {
t.Helper()
contrastLuminance := func(color string) float64 {
if len(color) != 7 || color[0] != '#' {
t.Fatalf("unsupported test color %q", color)
}
channels := make([]float64, 3)
for index := range channels {
value, err := strconv.ParseUint(color[1+index*2:3+index*2], 16, 8)
if err != nil {
t.Fatalf("parse test color %q: %v", color, err)
}
srgb := float64(value) / 255
if srgb <= 0.04045 {
channels[index] = srgb / 12.92
} else {
channels[index] = math.Pow((srgb+0.055)/1.055, 2.4)
}
}
return 0.2126*channels[0] + 0.7152*channels[1] + 0.0722*channels[2]
}
foregroundLuminance := contrastLuminance(foreground)
backgroundLuminance := contrastLuminance(background)
lighter := math.Max(foregroundLuminance, backgroundLuminance)
darker := math.Min(foregroundLuminance, backgroundLuminance)
return (lighter + 0.05) / (darker + 0.05)
}
func TestAppearanceBackfillsVersionSixFieldsForExistingSetting(t *testing.T) {
svc, db, _, _ := newAppearanceTestService(t)
legacy := model.SystemSetting{Key: appearanceSettingKey, ValueJSON: `{"schemaVersion":1,"brandName":"旧品牌","skinId":"classic"}`}

View File

@@ -13015,6 +13015,18 @@ html:not(.dark) .creation-history-drawer-root {
backdrop-filter: none !important;
}
/* Tooltip 必须使用同一套浮层背景/前景语义色。Ant Design 会让它继承
colorTextLightSolid(实心按钮文字色),深色主题下会形成深底深字。 */
:where(.ant-tooltip) {
--ant-tooltip-arrow-background-color: var(--popover) !important;
--ant-tooltip-overlay-color: var(--popover-foreground) !important;
}
:where(.ant-tooltip-container, .ant-tooltip-inner) {
background: var(--popover) !important;
color: var(--popover-foreground) !important;
}
:where(.ant-select-item-option, .ant-dropdown-menu-item, .ant-dropdown-menu-submenu-title, .ant-cascader-menu-item, .ant-mentions-dropdown-menu-item) {
min-height: 36px;
margin-block: 2px;

View File

@@ -155,6 +155,10 @@ describe("site appearance and editable skin library", () => {
expect(editorSource).toContain("后台菜单");
expect(globalStyles).toContain("--control-switch-checked-bg: #16a34a");
expect(globalStyles).toContain("--plugin-switch-checked-bg: var(--control-switch-checked-bg)");
expect(globalStyles).toContain("--ant-tooltip-arrow-background-color: var(--popover) !important");
expect(globalStyles).toContain("--ant-tooltip-overlay-color: var(--popover-foreground) !important");
expect(globalStyles).toContain(":where(.ant-tooltip-container, .ant-tooltip-inner)");
expect(globalStyles).toContain("color: var(--popover-foreground) !important");
expect(adminStyles).toContain("--admin-status-warning: var(--palette-status-warning)");
expect(adminStyles).toContain("border-radius: var(--menu-radius);");
});