From d89b55d1827be2430e14bf3d535b42ba37eaaa88 Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Thu, 24 Sep 2026 00:05:13 +0800 Subject: [PATCH] =?UTF-8?q?fix(auth):=20=E7=99=BB=E5=BD=95=E4=B8=BB?= =?UTF-8?q?=E9=A2=98=20-=20=E8=B7=9F=E9=9A=8F=E5=BA=94=E7=94=A8=E5=A4=96?= =?UTF-8?q?=E8=A7=82=E5=B9=B6=E8=A1=A5=E9=BD=90=E5=9B=9E=E5=BD=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/app/appearance_skins.go | 8 +- backend/internal/app/appearance_test.go | 21 +++ web/src/lib/skin-themes.ts | 10 +- web/src/pages/auth/auth-scene.tsx | 57 ++++---- web/src/pages/auth/forgot-password.tsx | 16 +-- web/src/pages/auth/login.tsx | 10 +- web/src/pages/auth/register.tsx | 20 +-- web/src/styles/globals.css | 163 +++++++++++++++++------ web/test/auth-theme.test.ts | 71 ++++++++++ 9 files changed, 276 insertions(+), 100 deletions(-) create mode 100644 web/test/auth-theme.test.ts diff --git a/backend/internal/app/appearance_skins.go b/backend/internal/app/appearance_skins.go index 8cae965f5..e1f05fdc7 100644 --- a/backend/internal/app/appearance_skins.go +++ b/backend/internal/app/appearance_skins.go @@ -143,7 +143,7 @@ func defaultAppearanceSkinThemes() []AppearanceSkinTheme { primary: "#087f76", primaryHover: "#076d66", primaryActive: "#095c57", primaryForeground: "#ffffff", selected: "#dff4f0", selectedHover: "#ccebe6", selectedActive: "#b9e3dd", selectedForeground: "#075f59", info: "#dd7a38", switchChecked: "#087f76", switchCheckedHover: "#076d66", switchCheckedHandle: "#ffffff", switchUnchecked: "#a8b7b9", switchUncheckedHover: "#899b9e", switchUncheckedHandle: "#ffffff", success: "#16866f", warning: "#c46722", danger: "#c83f3a", dangerHover: "#ad3532", dangerActive: "#922e2c", dangerForeground: "#ffffff", - workspace: "#ffffff", grid: "#e8f2f0", adminBackground: "#eef3f4", adminSurface: "#ffffff", adminSubtle: "#f5f9f9", adminStrong: "#dce9e8", authBackground: "#071a1d", authPanel: "#0b2226", authCard: "#0d272b", authAccent: "#72eadc", authMuted: "#8db9b4", + workspace: "#ffffff", grid: "#e8f2f0", adminBackground: "#eef3f4", adminSurface: "#ffffff", adminSubtle: "#f5f9f9", adminStrong: "#dce9e8", authBackground: "#f6f8f9", authPanel: "#edf4f3", authCard: "#ffffff", authAccent: "#087f76", authMuted: "#607079", }) studio.Tokens.Dark = tintAppearanceSkinMode(studio.Tokens.Dark, appearanceSkinPalette{ canvas: "#0b1215", surface: "#121d21", subtle: "#142428", raised: "#203a3b", overlay: "#172328", text: "#e7f4f2", muted: "#8ca6a7", border: "#294044", @@ -160,7 +160,7 @@ func defaultAppearanceSkinThemes() []AppearanceSkinTheme { primary: "#b94f2f", primaryHover: "#9e4128", primaryActive: "#843621", primaryForeground: "#fffaf6", selected: "#f8e0cf", selectedHover: "#f1d1bb", selectedActive: "#e9c1a6", selectedForeground: "#8c3d25", info: "#c58a3b", switchChecked: "#a84a2f", switchCheckedHover: "#bd5b3c", switchCheckedHandle: "#fffaf6", switchUnchecked: "#c7b3a7", switchUncheckedHover: "#aa9182", switchUncheckedHandle: "#fffdf9", success: "#4d7f50", warning: "#bd6819", danger: "#bd3d32", dangerHover: "#a33229", dangerActive: "#892a23", dangerForeground: "#fffaf6", - workspace: "#fffdf9", grid: "#f3e9e1", adminBackground: "#f6eee7", adminSurface: "#fffdf9", adminSubtle: "#faf3ed", adminStrong: "#ead8ca", authBackground: "#1f1410", authPanel: "#291813", authCard: "#361f17", authAccent: "#ffc08b", authMuted: "#b99d89", + workspace: "#fffdf9", grid: "#f3e9e1", adminBackground: "#f6eee7", adminSurface: "#fffdf9", adminSubtle: "#faf3ed", adminStrong: "#ead8ca", authBackground: "#fbf7f2", authPanel: "#f6ebe3", authCard: "#fffdf9", authAccent: "#b94f2f", authMuted: "#806b61", }) warm.Tokens.Dark = tintAppearanceSkinMode(warm.Tokens.Dark, appearanceSkinPalette{ canvas: "#1b1210", surface: "#291a16", subtle: "#33211b", raised: "#493027", overlay: "#31201a", text: "#f8e9df", muted: "#b89c8c", border: "#50372e", @@ -177,7 +177,7 @@ func defaultAppearanceSkinThemes() []AppearanceSkinTheme { primary: "#6656d9", primaryHover: "#5847c7", primaryActive: "#4939b2", primaryForeground: "#ffffff", selected: "#ebe8ff", selectedHover: "#ded9ff", selectedActive: "#d0c9ff", selectedForeground: "#4f3fb5", info: "#8f61e8", switchChecked: "#6656d9", switchCheckedHover: "#5847c7", switchCheckedHandle: "#ffffff", switchUnchecked: "#b4afc5", switchUncheckedHover: "#9891ae", switchUncheckedHandle: "#ffffff", success: "#2f966e", warning: "#b96f16", danger: "#c73559", dangerHover: "#ac2c4b", dangerActive: "#912640", dangerForeground: "#ffffff", - workspace: "#ffffff", grid: "#f0eef8", adminBackground: "#f1f0f7", adminSurface: "#ffffff", adminSubtle: "#f7f6fb", adminStrong: "#e4e0f1", authBackground: "#110d20", authPanel: "#17112b", authCard: "#211936", authAccent: "#b4a8ff", authMuted: "#958dad", + workspace: "#ffffff", grid: "#f0eef8", adminBackground: "#f1f0f7", adminSurface: "#ffffff", adminSubtle: "#f7f6fb", adminStrong: "#e4e0f1", authBackground: "#f8f7fc", authPanel: "#f0eefb", authCard: "#ffffff", authAccent: "#6656d9", authMuted: "#716a86", }) violet.Tokens.Dark = tintAppearanceSkinMode(violet.Tokens.Dark, appearanceSkinPalette{ canvas: "#0f0c19", surface: "#171321", subtle: "#201a2f", raised: "#302746", overlay: "#1c1734", text: "#f2efff", muted: "#a9a2bd", border: "#39304d", @@ -203,7 +203,7 @@ func defaultClassicAppearanceSkin() AppearanceSkinTheme { Control: "#ffffff", ControlHover: "#f5f5f5", ControlActive: "#ececec", ControlBorder: "#d1d1d1", ControlFocus: "#171717", ControlDisabledBackground: "#f2f2f2", ControlDisabledForeground: "#a3a3a3", SwitchChecked: "#16a34a", SwitchCheckedHover: "#15803d", SwitchCheckedHandle: "#ffffff", SwitchUnchecked: "#b8b8b8", SwitchUncheckedHover: "#9f9f9f", SwitchUncheckedHandle: "#ffffff", Primary: "#171717", PrimaryHover: "#303030", PrimaryActive: "#404040", PrimaryForeground: "#ffffff", Selected: "#e8e8e8", SelectedHover: "#dedede", SelectedActive: "#d5d5d5", SelectedForeground: "#171717", Icon: "#3f3f46", IconMuted: "#a1a1aa", IconActive: "#171717", Success: "#16a34a", Warning: "#d97706", Danger: "#dc2626", DangerHover: "#b91c1c", DangerActive: "#991b1b", DangerForeground: "#ffffff", Info: "#2563eb", Workspace: "#ffffff", WorkspaceGrid: "#f3f3f3", - AdminBackground: "#f3f4f6", AdminSurface: "#ffffff", AdminSubtle: "#f7f8fa", AdminStrong: "#eceff3", AuthBackground: "#08090c", AuthPanel: "#0b0c10", AuthCard: "#121318", AuthAccent: "#93c5fd", AuthMuted: "#8a8b91", + AdminBackground: "#f3f4f6", AdminSurface: "#ffffff", AdminSubtle: "#f7f8fa", AdminStrong: "#eceff3", AuthBackground: "#ffffff", AuthPanel: "#f7f7f7", AuthCard: "#ffffff", AuthAccent: "#2563eb", AuthMuted: "#737373", }, Dark: AppearanceSkinModeTokens{ Canvas: "#0a0a0a", Surface: "#181818", SurfaceSubtle: "#202020", SurfaceRaised: "#2a2a2a", Overlay: "#1f1f20", Text: "#f5f5f5", TextMuted: "#a3a3a3", Border: "#2d2d2d", diff --git a/backend/internal/app/appearance_test.go b/backend/internal/app/appearance_test.go index 7a11fe171..7956a5333 100644 --- a/backend/internal/app/appearance_test.go +++ b/backend/internal/app/appearance_test.go @@ -50,6 +50,27 @@ func TestAppearanceDefaultsPreserveBuiltInBrand(t *testing.T) { } } +func TestBuiltInAppearanceSkinAuthPalettesFollowMode(t *testing.T) { + for _, skin := range defaultAppearanceSkinThemes() { + if skin.Tokens.Light.AuthBackground == skin.Tokens.Dark.AuthBackground || skin.Tokens.Light.AuthCard == skin.Tokens.Dark.AuthCard { + t.Errorf("skin %s reuses dark auth surfaces in light mode", skin.ID) + } + for _, mode := range []struct { + name string + tokens AppearanceSkinModeTokens + }{ + {name: "light", tokens: skin.Tokens.Light}, + {name: "dark", tokens: skin.Tokens.Dark}, + } { + for label, foreground := range map[string]string{"text": mode.tokens.Text, "muted": mode.tokens.AuthMuted, "accent": mode.tokens.AuthAccent} { + if ratio := appearanceColorContrastRatio(t, foreground, mode.tokens.AuthCard); ratio < 4.5 { + t.Errorf("skin %s %s auth %s contrast = %.2f, want at least 4.5", skin.ID, mode.name, label, ratio) + } + } + } + } +} + func TestBuiltInAppearanceSkinTooltipPairsMeetContrast(t *testing.T) { for _, skin := range defaultAppearanceSkinThemes() { modes := []struct { diff --git a/web/src/lib/skin-themes.ts b/web/src/lib/skin-themes.ts index d08e62eb0..d41a60238 100644 --- a/web/src/lib/skin-themes.ts +++ b/web/src/lib/skin-themes.ts @@ -340,11 +340,11 @@ export const DEFAULT_CLASSIC_SKIN: SkinDefinition = { adminSurface: "#ffffff", adminSubtle: "#f6f6f6", adminStrong: "#ececec", - authBackground: "#08090c", - authPanel: "#0b0c10", - authCard: "#121318", - authAccent: "#93c5fd", - authMuted: "#8a8b91", + authBackground: "#ffffff", + authPanel: "#f7f7f7", + authCard: "#ffffff", + authAccent: "#2563eb", + authMuted: "#737373", }, dark: { canvas: "#0a0a0a", diff --git a/web/src/pages/auth/auth-scene.tsx b/web/src/pages/auth/auth-scene.tsx index 69ae1e1e2..e8b830ef9 100644 --- a/web/src/pages/auth/auth-scene.tsx +++ b/web/src/pages/auth/auth-scene.tsx @@ -1,5 +1,5 @@ import { motion, useReducedMotion } from "motion/react"; -import { ConfigProvider, Tabs } from "antd"; +import { Tabs } from "antd"; import { ArrowLeft, Play } from "lucide-react"; import { useEffect, useRef, useState } from "react"; import { Link, Outlet, useLocation, useNavigate } from "react-router"; @@ -7,8 +7,8 @@ import { Link, Outlet, useLocation, useNavigate } from "react-router"; import { BrandLogo } from "@/components/brand/brand-logo"; import { SiteComplianceFooter } from "@/components/layout/site-compliance-footer"; import { aceternityMotion } from "@/lib/aceternity-motion"; -import { getAntThemeConfig } from "@/lib/app-theme"; import { brandStudioLabel, useAppearanceStore } from "@/stores/use-appearance-store"; +import { useThemeStore } from "@/stores/use-theme-store"; const AUTH_TABS = [ { key: "login", label: "登录" }, @@ -48,6 +48,7 @@ export function LinuxDOIcon() { export function AuthScene() { const appearance = useAppearanceStore((state) => state.appearance); + const theme = useThemeStore((state) => state.theme); const location = useLocation(); const navigate = useNavigate(); const reducedMotion = useReducedMotion(); @@ -74,18 +75,18 @@ export function AuthScene() { }; return ( -
+
-
+
{videoActive && appearance.authVideoUrl ?
@@ -115,25 +116,23 @@ export function AuthScene() { transition={{ duration: aceternityMotion.duration.panel, ease: aceternityMotion.easing.enter }} className="my-auto w-full max-w-[460px]" > - -
-
-
-

{copy.eyebrow}

-

{copy.title}

-

{copy.description}

-
- {!recovery ? ( -
- navigate({ pathname: key === "register" ? "/register" : "/login", search: location.search })} /> -
- ) : null} -
- +
+
+
+

{copy.eyebrow}

+

{copy.title}

+

{copy.description}

+
+ {!recovery ? ( +
+ navigate({ pathname: key === "register" ? "/register" : "/login", search: location.search })} />
-
-
- + ) : null} +
+ +
+
+
diff --git a/web/src/pages/auth/forgot-password.tsx b/web/src/pages/auth/forgot-password.tsx index 10fc8edf1..c1c5b44ed 100644 --- a/web/src/pages/auth/forgot-password.tsx +++ b/web/src/pages/auth/forgot-password.tsx @@ -98,7 +98,7 @@ export default function ForgotPasswordPage() { } + prefix={} value={email} onChange={(event) => setEmail(event.target.value)} placeholder="请输入绑定邮箱" @@ -120,7 +120,7 @@ export default function ForgotPasswordPage() {
- } value={email} readOnly autoComplete="email" /> + } value={email} readOnly autoComplete="email" /> @@ -131,7 +131,7 @@ export default function ForgotPasswordPage() { } + prefix={} value={emailCode} onChange={(event) => setEmailCode(event.target.value.replace(/\D/g, "").slice(0, 6))} placeholder="6 位验证码" @@ -149,7 +149,7 @@ export default function ForgotPasswordPage() { } + prefix={} value={password} onChange={(event) => setPassword(event.target.value)} placeholder="至少 8 位" @@ -162,7 +162,7 @@ export default function ForgotPasswordPage() { } + prefix={} value={confirmPassword} onChange={(event) => setConfirmPassword(event.target.value)} placeholder="再次输入密码" @@ -183,7 +183,7 @@ export default function ForgotPasswordPage() { function AuthField({ label, htmlFor, children }: { label: string; htmlFor: string; children: ReactNode }) { return (
-