fix: 分站加载动画跟随主题色

- 添加 --page-bg/--page-spinner CSS 变量,各主题定义自己的颜色
- body 背景改用 CSS 变量,不再硬编码黑色
- SiteContext 获取站点信息后立即应用主题类到 body
- localStorage 缓存主题类,刷新时在 React 加载前恢复,防止闪烁
- 浅色主题(clean/corporate/claude)加载时显示白色/米色背景+对应色 spinner

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
abingyydsandClaude Opus 4.6 committed 2026-03-27 21:29:53 +09:00
1 parent 16c42412bc
commit 91e673aabd
36 files changed
+419 -12

No files matched your search

+3 -2
View File
@@ -13,8 +13,9 @@ const Topup = lazy(() => import('./pages/Topup'));
const Logs = lazy(() => import('./pages/Logs'));
const Loading = () => (
<div className="flex items-center justify-center min-h-screen">
<div className="w-8 h-8 border-2 border-brand-500/30 border-t-brand-500 rounded-full animate-spin" />
<div className="flex items-center justify-center min-h-screen" style={{ background: 'var(--page-bg)' }}>
<div className="w-8 h-8 rounded-full animate-spin"
style={{ border: '2px solid var(--page-spinner-track)', borderTopColor: 'var(--page-spinner)' }} />
</div>
);
+19
View File
@@ -3,6 +3,23 @@ import { getSiteInfo } from '../api';
const SiteContext = createContext(null);
// Map theme template name → CSS class(es) to apply on <body>
const themeClassMap = {
starter: '',
default: 'theme-default',
dark: 'theme-dark',
minimal: 'theme-minimal',
clean: 'theme-light',
corporate: 'theme-light',
claude: 'theme-light theme-claude',
};
function applyThemeClass(themeName) {
const cls = themeClassMap[themeName] || '';
document.body.className = cls + (cls ? ' ' : '') + 'antialiased';
try { localStorage.setItem('dist-theme-class', cls); } catch(e) {}
}
export function SiteProvider({ children }) {
const [site, setSite] = useState(null);
const [loading, setLoading] = useState(true);
@@ -12,6 +29,8 @@ export function SiteProvider({ children }) {
.then((res) => {
if (res.data.success) {
setSite(res.data.data);
// Apply theme class to body immediately
applyThemeClass(res.data.data?.theme_template);
// Update page title
if (res.data.data?.name) {
document.title = res.data.data.name;
+3 -2
View File
@@ -39,8 +39,9 @@ const themeRegistry = {
// Prevents theme flash (rendering default theme before API returns the real one)
function ThemeLoading() {
return (
<div className="flex items-center justify-center min-h-screen bg-gray-950">
<div className="w-8 h-8 border-2 border-white/20 border-t-white/70 rounded-full animate-spin" />
<div className="flex items-center justify-center min-h-screen" style={{ background: 'var(--page-bg)' }}>
<div className="w-8 h-8 rounded-full animate-spin"
style={{ border: '2px solid var(--page-spinner-track)', borderTopColor: 'var(--page-spinner)' }} />
</div>
);
}
+17 -1
View File
@@ -5,6 +5,10 @@
@layer base {
:root {
--brand: 99 102 241;
/* Page background */
--page-bg: #000000;
--page-spinner: rgba(255,255,255,0.7);
--page-spinner-track: rgba(255,255,255,0.2);
/* Text colors */
--page-text: 255 255 255;
--page-text-secondary: 163 163 163;
@@ -32,7 +36,8 @@
}
body {
@apply bg-black text-white;
background: var(--page-bg);
color: rgb(var(--page-text));
font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11';
}
@@ -56,6 +61,9 @@
/* Light theme variable overrides */
.theme-light {
--page-bg: #ffffff;
--page-spinner: rgba(99,102,241,0.8);
--page-spinner-track: rgba(99,102,241,0.2);
--page-text: 17 24 39;
--page-text-secondary: 107 114 128;
--page-text-muted: 156 163 175;
@@ -80,6 +88,9 @@
}
.theme-claude {
--page-bg: #FAF6F1;
--page-spinner: rgba(217,119,87,0.8);
--page-spinner-track: rgba(217,119,87,0.2);
--page-link: 217 119 87;
--page-card-bg: rgba(255,255,255,0.7);
--page-card-border: #E8DDD0;
@@ -88,6 +99,11 @@
--page-divider: #E8DDD0;
--page-inset: rgba(61,48,36,0.06);
}
/* Per-dark-theme background overrides */
.theme-default { --page-bg: #09090b; }
.theme-dark { --page-bg: #030712; }
.theme-minimal { --page-bg: #0a0a0a; }
}
@layer components {