Redesign v2: Solar Momentum — bold, bright, energetic
Design philosophy informed by skills review: - canvas-design: Named aesthetic movement, craftsmanship emphasis - frontend-design: Bold direction, distinctive typography, avoid AI slop - frontend-patterns: Component composition, proper hooks - theme-factory: Focused 4-color palettes with clear hierarchy Changes: - Palette: Deep orange (#FF5722) + vivid green (#00C853) + electric blue + purple - Typography: Outfit (display, 900 weight) + Plus Jakarta Sans (body) - Background: Warm gradient (peach → cream → light blue) - Gate banner: 2.8rem hero numbers, thick gradient progress bars - Activity checklist: Colored icon badges per category - Cards: Larger radius (20px), warm shadows, tinted section backgrounds - Login: Bold gradient hero (orange → dark), fire emoji brand - Navigation: Orange active indicator bar, frosted glass backdrop - Week view: Purple-themed hero with large stats - Buttons: More shadow depth, active press scale animation - All CSS variables renamed to semantic system (--orange, --green, --text-2) - Zero orphaned old variable references
This commit is contained in:
parent
22f5e8fae3
commit
080b7856b7
9 changed files with 566 additions and 552 deletions
|
|
@ -16,59 +16,58 @@ export default function Login() {
|
|||
setError('')
|
||||
setLoading(true)
|
||||
try {
|
||||
let data
|
||||
if (mode === 'login') {
|
||||
data = await api.login(username, password)
|
||||
} else {
|
||||
data = await api.register(username, password, displayName || username)
|
||||
}
|
||||
const data = mode === 'login'
|
||||
? await api.login(username, password)
|
||||
: await api.register(username, password, displayName || username)
|
||||
setToken(data.access_token)
|
||||
const status = await api.onboardingStatus()
|
||||
navigate(status.phase1_completed ? '/' : '/onboarding')
|
||||
} catch (err) {
|
||||
setError(err.message)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
} catch (err) { setError(err.message) }
|
||||
finally { setLoading(false) }
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ minHeight: '100dvh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: '24px', background: 'linear-gradient(160deg, #fff7f0 0%, #faf8f5 40%, #f0f4ff 100%)' }}>
|
||||
<div style={{ width: '100%', maxWidth: '400px' }}>
|
||||
|
||||
{/* Logo & branding */}
|
||||
<div style={{ textAlign: 'center', marginBottom: '36px' }}>
|
||||
<div style={{
|
||||
width: '72px', height: '72px', borderRadius: '20px', margin: '0 auto 16px',
|
||||
background: 'linear-gradient(135deg, #ff6b35 0%, #ff8f5e 100%)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
fontSize: '2rem', boxShadow: '0 8px 24px rgba(255, 107, 53, 0.3)',
|
||||
}}>🏆</div>
|
||||
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: '2rem', fontWeight: 800, letterSpacing: '-0.03em', color: 'var(--text)' }}>
|
||||
<div style={{
|
||||
minHeight: '100dvh',
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
|
||||
padding: '24px',
|
||||
background: 'linear-gradient(170deg, #FF8A65 0%, #FF5722 30%, #E64A19 60%, #1B1B2F 100%)',
|
||||
}}>
|
||||
<div style={{ width: '100%', maxWidth: '380px' }}>
|
||||
{/* Brand */}
|
||||
<div style={{ textAlign: 'center', marginBottom: '32px', color: '#fff' }}>
|
||||
<div style={{ fontSize: '3.2rem', marginBottom: '8px', filter: 'drop-shadow(0 4px 12px rgba(0,0,0,0.2))' }}>🔥</div>
|
||||
<h1 style={{ fontFamily: 'var(--font-display)', fontSize: '2.2rem', fontWeight: 900, letterSpacing: '-0.03em' }}>
|
||||
Fitness Rewards
|
||||
</h1>
|
||||
<p style={{ color: 'var(--text-muted)', marginTop: '6px', fontSize: '0.95rem' }}>
|
||||
Earn your rewards. Every day.
|
||||
<p style={{ opacity: 0.8, marginTop: '4px', fontSize: '0.95rem', fontWeight: 500 }}>
|
||||
Earn your rewards. Every single day.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Card */}
|
||||
<div className="card" style={{ padding: '28px', boxShadow: 'var(--shadow-lg)' }}>
|
||||
{/* Tab toggle */}
|
||||
{/* Form card */}
|
||||
<div style={{
|
||||
background: 'var(--card)', borderRadius: 'var(--r-lg)', padding: '28px',
|
||||
boxShadow: '0 20px 60px rgba(0,0,0,0.3)',
|
||||
}}>
|
||||
{/* Toggle */}
|
||||
<div style={{
|
||||
display: 'flex', gap: '4px', marginBottom: '24px', padding: '4px',
|
||||
background: 'var(--bg)', borderRadius: 'var(--radius-full)',
|
||||
display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '4px',
|
||||
background: 'rgba(0,0,0,0.04)', borderRadius: 'var(--r)', padding: '4px', marginBottom: '24px',
|
||||
}}>
|
||||
<button
|
||||
className={mode === 'login' ? 'btn-primary btn-full' : 'btn-ghost btn-full'}
|
||||
onClick={() => setMode('login')} type="button"
|
||||
style={{ borderRadius: 'var(--radius-full)' }}
|
||||
>Sign In</button>
|
||||
<button
|
||||
className={mode === 'register' ? 'btn-primary btn-full' : 'btn-ghost btn-full'}
|
||||
onClick={() => setMode('register')} type="button"
|
||||
style={{ borderRadius: 'var(--radius-full)' }}
|
||||
>Sign Up</button>
|
||||
{['login', 'register'].map(m => (
|
||||
<button key={m} type="button"
|
||||
onClick={() => setMode(m)}
|
||||
style={{
|
||||
borderRadius: 'var(--r-sm)', padding: '10px',
|
||||
fontWeight: 700, fontSize: '0.85rem',
|
||||
background: mode === m ? 'var(--orange)' : 'transparent',
|
||||
color: mode === m ? '#fff' : 'var(--text-2)',
|
||||
boxShadow: mode === m ? 'var(--shadow-orange)' : 'none',
|
||||
}}>
|
||||
{m === 'login' ? 'Sign In' : 'Sign Up'}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{error && <div className="error-msg">{error}</div>}
|
||||
|
|
@ -76,11 +75,11 @@ export default function Login() {
|
|||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label className="form-label">Username</label>
|
||||
<input value={username} onChange={e => setUsername(e.target.value)} required autoComplete="username" placeholder="your username" />
|
||||
<input value={username} onChange={e => setUsername(e.target.value)} required autoComplete="username" />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="form-label">Password</label>
|
||||
<input type="password" value={password} onChange={e => setPassword(e.target.value)} required autoComplete="current-password" placeholder="your password" />
|
||||
<input type="password" value={password} onChange={e => setPassword(e.target.value)} required autoComplete="current-password" />
|
||||
</div>
|
||||
{mode === 'register' && (
|
||||
<div className="form-group">
|
||||
|
|
@ -88,8 +87,9 @@ export default function Login() {
|
|||
<input value={displayName} onChange={e => setDisplayName(e.target.value)} placeholder="What should we call you?" />
|
||||
</div>
|
||||
)}
|
||||
<button type="submit" className="btn-primary btn-full" disabled={loading} style={{ padding: '14px', fontSize: '1rem', marginTop: '4px' }}>
|
||||
{loading ? 'Working...' : mode === 'login' ? 'Sign In' : 'Create Account'}
|
||||
<button type="submit" className="btn-primary btn-full" disabled={loading}
|
||||
style={{ padding: '14px', fontSize: '0.95rem', marginTop: '8px', borderRadius: 'var(--r)' }}>
|
||||
{loading ? '...' : mode === 'login' ? 'Sign In' : 'Create Account'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue