Skip to content

Commit 300bb35

Browse files
nanyuantingfengtheonlyhennygod
authored andcommitted
feat(web): add theme system with CSS variables and settings modal
- Add ThemeContext with light/dark/system theme support - Migrate all hardcoded colors to CSS variables - Add SettingsModal for theme customization - Add font loader for dynamic font selection - Add i18n support for Chinese and Turkish locales - Fix accessibility: add aria-live to pairing error message
1 parent c1b2fce commit 300bb35

23 files changed

Lines changed: 2025 additions & 1001 deletions

web/src/App.tsx

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Routes, Route, Navigate } from 'react-router-dom';
2-
import { useState, useEffect, createContext, useContext, Component } from 'react';
3-
import type { ReactNode, ErrorInfo } from 'react';
2+
import { useState, useEffect, createContext, useContext, Component, type ReactNode, type ErrorInfo } from 'react';
3+
import { ThemeProvider } from './contexts/ThemeContext';
44
import Layout from './components/layout/Layout';
55
import Dashboard from './pages/Dashboard';
66
import AgentChat from './pages/AgentChat';
@@ -61,19 +61,19 @@ export class ErrorBoundary extends Component<
6161
if (this.state.error) {
6262
return (
6363
<div className="p-6">
64-
<div className="bg-gray-900 border border-red-700 rounded-xl p-6 w-full max-w-lg">
65-
<h2 className="text-lg font-semibold text-red-400 mb-2">
64+
<div className="card p-6 w-full max-w-lg" style={{ borderColor: 'rgba(239, 68, 68, 0.3)' }}>
65+
<h2 className="text-lg font-semibold mb-2" style={{ color: 'var(--color-status-error)' }}>
6666
Something went wrong
6767
</h2>
68-
<p className="text-gray-400 text-sm mb-4">
68+
<p className="text-sm mb-4" style={{ color: 'var(--pc-text-muted)' }}>
6969
A render error occurred. Check the browser console for details.
7070
</p>
71-
<pre className="text-xs text-red-300 bg-gray-800 rounded p-3 overflow-x-auto whitespace-pre-wrap break-all">
71+
<pre className="text-xs rounded-lg p-3 overflow-x-auto whitespace-pre-wrap break-all font-mono" style={{ background: 'var(--pc-bg-base)', color: 'var(--color-status-error)' }}>
7272
{this.state.error.message}
7373
</pre>
7474
<button
7575
onClick={() => this.setState({ error: null })}
76-
className="mt-6 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors"
76+
className="btn-electric mt-6 px-4 py-2 text-sm font-medium"
7777
>
7878
Try again
7979
</button>
@@ -125,36 +125,30 @@ function PairingDialog({ onPair }: { onPair: (code: string) => Promise<void> })
125125
};
126126

127127
return (
128-
<div className="min-h-screen flex items-center justify-center" style={{ background: 'radial-gradient(ellipse at center, #0a0a20 0%, #050510 70%)' }}>
128+
<div className="min-h-screen flex items-center justify-center" style={{ background: 'var(--pc-bg-base)' }}>
129129
{/* Ambient glow */}
130-
<div className="fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-[500px] h-[500px] rounded-full opacity-20 pointer-events-none" style={{ background: 'radial-gradient(circle, #0080ff 0%, transparent 70%)' }} />
131-
132-
<div className="relative glass-card p-8 w-full max-w-md animate-fade-in-scale">
133-
{/* Top glow accent */}
134-
<div className="absolute -top-px left-1/4 right-1/4 h-px" style={{ background: 'linear-gradient(90deg, transparent, #0080ff, transparent)' }} />
130+
<div className="relative surface-panel p-8 w-full max-w-md animate-fade-in-scale">
135131

136132
<div className="text-center mb-8">
137133
<img
138134
src="/_app/zeroclaw-trans.png"
139135
alt="ZeroClaw"
140136
className="h-20 w-20 rounded-2xl object-cover mx-auto mb-4 animate-float"
141-
style={{ boxShadow: '0 0 30px rgba(0,128,255,0.3)' }}
137+
onError={(e) => { e.currentTarget.style.display = 'none'; }}
142138
/>
143-
<h1 className="text-2xl font-bold text-gradient-blue mb-2">ZeroClaw</h1>
144-
{displayCode ? (
145-
<p className="text-[#556080] text-sm">Your pairing code</p>
146-
) : (
147-
<p className="text-[#556080] text-sm">Enter the pairing code from your terminal</p>
148-
)}
139+
<h1 className="text-2xl font-bold mb-2 text-gradient-blue">ZeroClaw</h1>
140+
<p className="text-sm" style={{ color: 'var(--pc-text-muted)' }}>
141+
{displayCode ? 'Your pairing code' : 'Enter the pairing code from your terminal'}
142+
</p>
149143
</div>
150144

151145
{/* Show the pairing code if available (localhost) */}
152146
{!codeLoading && displayCode && (
153-
<div className="mb-6 p-4 rounded-xl text-center" style={{ background: 'rgba(0,128,255,0.08)', border: '1px solid rgba(0,128,255,0.2)' }}>
154-
<div className="text-4xl font-mono font-bold tracking-[0.4em] text-white py-2">
147+
<div className="mb-6 p-4 rounded-2xl text-center border" style={{ background: 'var(--pc-accent-glow)', borderColor: 'var(--pc-accent-dim)' }}>
148+
<div className="text-4xl font-mono font-bold tracking-[0.4em] py-2" style={{ color: 'var(--pc-text-primary)' }}>
155149
{displayCode}
156150
</div>
157-
<p className="text-[#556080] text-xs mt-2">Enter this code below or on another device</p>
151+
<p className="text-xs mt-2" style={{ color: 'var(--pc-text-muted)' }}>Enter this code below or on another device</p>
158152
</div>
159153
)}
160154

@@ -169,7 +163,7 @@ function PairingDialog({ onPair }: { onPair: (code: string) => Promise<void> })
169163
autoFocus
170164
/>
171165
{error && (
172-
<p className="text-[#ff4466] text-sm mb-4 text-center animate-fade-in" aria-live="polite">{error}</p>
166+
<p aria-live="polite" className="text-sm mb-4 text-center animate-fade-in" style={{ color: 'var(--color-status-error)' }}>{error}</p>
173167
)}
174168
<button
175169
type="submit"
@@ -210,10 +204,10 @@ function AppContent() {
210204

211205
if (loading) {
212206
return (
213-
<div className="min-h-screen flex items-center justify-center" style={{ background: 'radial-gradient(ellipse at center, #0a0a20 0%, #050510 70%)' }}>
207+
<div className="min-h-screen flex items-center justify-center" style={{ background: 'var(--pc-bg-base)' }}>
214208
<div className="flex flex-col items-center gap-4 animate-fade-in">
215-
<div className="h-10 w-10 border-2 border-[#0080ff30] border-t-[#0080ff] rounded-full animate-spin" />
216-
<p className="text-[#556080] text-sm">Connecting...</p>
209+
<div className="h-10 w-10 border-2 rounded-full animate-spin" style={{ borderColor: 'var(--pc-border)', borderTopColor: 'var(--pc-accent)' }} />
210+
<p className="text-sm" style={{ color: 'var(--pc-text-muted)' }}>Connecting...</p>
217211
</div>
218212
</div>
219213
);
@@ -250,7 +244,9 @@ function AppContent() {
250244
export default function App() {
251245
return (
252246
<AuthProvider>
253-
<AppContent />
247+
<ThemeProvider>
248+
<AppContent />
249+
</ThemeProvider>
254250
</AuthProvider>
255251
);
256252
}

0 commit comments

Comments
 (0)