design: rewrite homepage with proper light/dark mode using Tailwind semantic tokens
Docker Build / Build and Push Docker Image (push) Successful in 4m23s

This commit is contained in:
2026-06-14 12:20:39 +08:00
parent 4e335a9997
commit c175a41a2b
5 changed files with 76 additions and 77 deletions
+8 -8
View File
@@ -35,14 +35,14 @@ export function CTA(props: CTAProps) {
}
return (
<section className='relative z-10 overflow-hidden bg-[#0a0e1a] px-6 py-24 md:py-32'>
<section className='relative z-10 overflow-hidden bg-background px-6 py-24 md:py-32'>
{/* Subtle glow */}
<div
aria-hidden
className='absolute left-1/2 top-1/2 -z-10 h-[400px] w-[600px] -translate-x-1/2 -translate-y-1/2 opacity-20'
className='absolute left-1/2 top-1/2 -z-10 h-[400px] w-[600px] -translate-x-1/2 -translate-y-1/2 opacity-[0.06] dark:opacity-[0.12]'
style={{
background:
'radial-gradient(ellipse 50% 50% at 50% 50%, rgba(14,165,233,0.3) 0%, transparent 70%)',
'radial-gradient(ellipse 50% 50% at 50% 50%, oklch(0.72 0.17 200 / 100%) 0%, transparent 70%)',
}}
/>
@@ -50,21 +50,21 @@ export function CTA(props: CTAProps) {
className='mx-auto max-w-2xl text-center'
animation='scale-in'
>
<h2 className='text-2xl leading-tight font-bold tracking-tight text-white md:text-4xl'>
<h2 className='text-2xl leading-tight font-bold tracking-tight text-foreground md:text-4xl'>
{t('Ready to simplify')}
<br />
<span className='bg-gradient-to-r from-cyan-400 to-emerald-400 bg-clip-text text-transparent'>
<span className='bg-gradient-to-r from-sky-500 to-teal-500 bg-clip-text text-transparent dark:from-sky-400 dark:to-teal-400'>
{t('your AI integration?')}
</span>
</h2>
<p className='mx-auto mt-5 max-w-md text-sm leading-relaxed text-slate-400 md:text-base'>
<p className='mx-auto mt-5 max-w-md text-sm leading-relaxed text-muted-foreground md:text-base'>
{t(
'Deploy your own gateway and start routing requests through your configured upstream services.'
)}
</p>
<div className='mt-8 flex items-center justify-center gap-3'>
<Button
className='group rounded-lg border-0 bg-cyan-500 text-white hover:bg-cyan-400'
className='group rounded-lg bg-sky-600 text-white hover:bg-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400'
render={<Link to='/sign-up' />}
>
{t('Get Started')}
@@ -72,7 +72,7 @@ export function CTA(props: CTAProps) {
</Button>
<Button
variant='outline'
className='rounded-lg border-white/[0.1] bg-white/[0.04] text-slate-300 hover:border-white/[0.2] hover:bg-white/[0.08] hover:text-white'
className='rounded-lg'
render={<Link to='/pricing' />}
>
{t('View Pricing')}
@@ -45,14 +45,14 @@ export function Features(_props: FeaturesProps) {
'Optimized network architecture ensures millisecond response times'
),
span: 'md:col-span-2',
icon: <Zap className='size-4 text-cyan-400' />,
icon: <Zap className='size-4 text-sky-600 dark:text-sky-400' />,
visual: (
<div className='mt-4 grid grid-cols-3 gap-2'>
{['OpenAI', 'Claude', 'Gemini', 'DeepSeek', 'Qwen', 'Llama'].map(
(name) => (
<div
key={name}
className='flex items-center justify-center rounded-lg border border-white/[0.06] bg-white/[0.02] px-3 py-2 text-xs text-slate-400 transition-colors duration-300 hover:border-cyan-500/20 hover:text-cyan-400'
className='flex items-center justify-center rounded-lg border border-border bg-muted/50 px-3 py-2 text-xs text-muted-foreground transition-colors duration-300 hover:border-sky-500/30 hover:text-sky-600 dark:bg-muted/30 dark:hover:border-sky-400/30 dark:hover:text-sky-400'
>
{name}
</div>
@@ -69,13 +69,13 @@ export function Features(_props: FeaturesProps) {
'Enterprise-grade security with comprehensive permission management'
),
span: 'md:col-span-1',
icon: <Shield className='size-4 text-emerald-400' />,
icon: <Shield className='size-4 text-emerald-600 dark:text-emerald-400' />,
visual: (
<div className='mt-4 flex items-center justify-center'>
<div className='relative'>
<div className='flex size-16 items-center justify-center rounded-2xl border border-emerald-500/20 bg-emerald-500/5'>
<Shield
className='size-7 text-emerald-500/70'
className='size-7 text-emerald-600/70 dark:text-emerald-500/70'
strokeWidth={1.5}
/>
</div>
@@ -104,7 +104,7 @@ export function Features(_props: FeaturesProps) {
title: t('Global Coverage'),
desc: t('Multi-region deployment for stable global access'),
span: 'md:col-span-1',
icon: <Globe className='size-4 text-blue-400' />,
icon: <Globe className='size-4 text-blue-600 dark:text-blue-400' />,
visual: (
<div className='mt-4 space-y-2'>
{[t('Load Balancing'), t('Rate Limiting'), t('Cost Tracking')].map(
@@ -113,14 +113,14 @@ export function Features(_props: FeaturesProps) {
<div
className={`flex size-6 items-center justify-center rounded-full text-[10px] font-bold ${
i === 1
? 'border border-cyan-500/30 bg-cyan-500/20 text-cyan-400'
: 'border border-white/[0.08] bg-white/[0.03] text-slate-500'
? 'border border-sky-500/30 bg-sky-500/10 text-sky-600 dark:border-sky-400/30 dark:bg-sky-400/10 dark:text-sky-400'
: 'border border-border bg-muted/50 text-muted-foreground dark:bg-muted/30'
}`}
>
{i + 1}
</div>
<div className='h-px flex-1 bg-white/[0.06]' />
<span className='text-xs text-slate-500'>{step}</span>
<div className='h-px flex-1 bg-border' />
<span className='text-xs text-muted-foreground'>{step}</span>
</div>
)
)}
@@ -133,21 +133,21 @@ export function Features(_props: FeaturesProps) {
title: t('Developer Friendly'),
desc: t('Compatible API routes for common AI application workflows'),
span: 'md:col-span-2',
icon: <Code className='size-4 text-cyan-400' />,
icon: <Code className='size-4 text-sky-600 dark:text-sky-400' />,
visual: (
<div className='mt-4 flex items-center gap-3'>
<div className='flex -space-x-2'>
{['API', 'SDK', 'CLI', 'Docs'].map((n) => (
<div
key={n}
className='flex size-8 items-center justify-center rounded-full border-2 border-[#0d1117] bg-white/[0.06] text-[9px] font-bold text-slate-400'
className='flex size-8 items-center justify-center rounded-full border-2 border-background bg-muted/50 text-[9px] font-bold text-muted-foreground dark:bg-muted/30'
>
{n}
</div>
))}
</div>
<div className='flex items-center gap-1.5 text-xs text-slate-500'>
<Code className='size-3.5 text-cyan-400' />
<div className='flex items-center gap-1.5 text-xs text-muted-foreground'>
<Code className='size-3.5 text-sky-600 dark:text-sky-400' />
{t('Multi-protocol Compatible')}
</div>
</div>
@@ -179,13 +179,13 @@ export function Features(_props: FeaturesProps) {
]
return (
<section className='relative z-10 bg-[#0a0e1a] px-6 py-24 md:py-32'>
<section className='relative z-10 bg-background px-6 py-24 md:py-32'>
<div className='mx-auto max-w-5xl'>
<AnimateInView className='mb-16 max-w-lg'>
<p className='mb-3 text-xs font-medium tracking-widest text-cyan-400/60 uppercase'>
<p className='mb-3 text-xs font-medium tracking-widest text-sky-600/60 uppercase dark:text-sky-400/60'>
{t('Core Features')}
</p>
<h2 className='text-2xl leading-tight font-bold tracking-tight text-white md:text-3xl'>
<h2 className='text-2xl leading-tight font-bold tracking-tight text-foreground md:text-3xl'>
{t('Built for developers,')}
<br />
{t('designed for scale')}
@@ -193,21 +193,21 @@ export function Features(_props: FeaturesProps) {
</AnimateInView>
{/* Bento grid */}
<div className='grid gap-px overflow-hidden rounded-xl border border-white/[0.06] bg-white/[0.06] md:grid-cols-3'>
<div className='grid gap-px overflow-hidden rounded-xl border border-border bg-border md:grid-cols-3'>
{features.map((f, i) => (
<AnimateInView
key={f.id}
delay={i * 100}
animation='scale-in'
className={`bg-[#0d1117] p-7 transition-colors duration-300 md:p-8 ${f.span}`}
className={`bg-card p-7 transition-colors duration-300 md:p-8 ${f.span}`}
>
<div className='mb-3 flex items-center gap-3'>
<span className='flex size-7 items-center justify-center rounded-md border border-white/[0.08] bg-white/[0.03] text-[10px] font-semibold tabular-nums text-slate-500'>
<span className='flex size-7 items-center justify-center rounded-md border border-border bg-muted/50 text-[10px] font-semibold tabular-nums text-muted-foreground dark:bg-muted/30'>
{f.num}
</span>
<h3 className='text-sm font-semibold text-white'>{f.title}</h3>
<h3 className='text-sm font-semibold text-foreground'>{f.title}</h3>
</div>
<p className='text-sm leading-relaxed text-slate-400'>
<p className='text-sm leading-relaxed text-muted-foreground'>
{f.desc}
</p>
{f.visual}
@@ -224,13 +224,13 @@ export function Features(_props: FeaturesProps) {
animation='fade-up'
className='flex flex-col items-center text-center'
>
<div className='mb-3 flex size-12 items-center justify-center rounded-xl border border-white/[0.06] bg-white/[0.03] text-slate-400'>
<div className='mb-3 flex size-12 items-center justify-center rounded-xl border border-border bg-muted/50 text-muted-foreground dark:bg-muted/30'>
{f.icon}
</div>
<h3 className='mb-1.5 text-sm font-semibold text-white'>
<h3 className='mb-1.5 text-sm font-semibold text-foreground'>
{f.title}
</h3>
<p className='max-w-[200px] text-xs leading-relaxed text-slate-500'>
<p className='max-w-[200px] text-xs leading-relaxed text-muted-foreground'>
{f.desc}
</p>
</AnimateInView>
+33 -34
View File
@@ -48,34 +48,33 @@ export function Hero(props: HeroProps) {
return (
<section className='relative z-10 overflow-hidden'>
{/* Dark background with subtle grid */}
<div className='absolute inset-0 -z-10 bg-[#0a0e1a]'>
{/* Background layer */}
<div className='absolute inset-0 -z-10 bg-background'>
{/* Grid pattern */}
<div
aria-hidden
className='absolute inset-0 opacity-[0.04]'
className='absolute inset-0 opacity-[0.03] dark:opacity-[0.04]'
style={{
backgroundImage:
'linear-gradient(rgba(56,189,248,0.3) 1px, transparent 1px), linear-gradient(90deg, rgba(56,189,248,0.3) 1px, transparent 1px)',
'linear-gradient(var(--foreground) 1px, transparent 1px), linear-gradient(90deg, var(--foreground) 1px, transparent 1px)',
backgroundSize: '64px 64px',
}}
/>
{/* Radial glow - blue */}
{/* Radial glow */}
<div
aria-hidden
className='absolute left-1/2 top-0 -z-10 h-[600px] w-[800px] -translate-x-1/2 -translate-y-1/2 opacity-30'
className='absolute left-1/2 top-0 -z-10 h-[600px] w-[800px] -translate-x-1/2 -translate-y-1/2 opacity-[0.06] dark:opacity-[0.15]'
style={{
background:
'radial-gradient(ellipse 60% 50% at 50% 50%, rgba(14,165,233,0.25) 0%, transparent 70%)',
'radial-gradient(ellipse 60% 50% at 50% 50%, oklch(0.72 0.17 200 / 100%) 0%, transparent 70%)',
}}
/>
{/* Radial glow - green */}
<div
aria-hidden
className='absolute right-0 top-1/3 -z-10 h-[400px] w-[500px] opacity-20'
className='absolute right-0 top-1/3 -z-10 h-[400px] w-[500px] opacity-[0.04] dark:opacity-[0.1]'
style={{
background:
'radial-gradient(ellipse 50% 50% at 50% 50%, rgba(16,185,129,0.3) 0%, transparent 70%)',
'radial-gradient(ellipse 50% 50% at 50% 50%, oklch(0.7 0.15 160 / 100%) 0%, transparent 70%)',
}}
/>
</div>
@@ -84,7 +83,7 @@ export function Hero(props: HeroProps) {
<div className='flex flex-col items-center text-center'>
{/* Status badge */}
<div
className='landing-animate-fade-up mb-8 inline-flex items-center gap-2 rounded-full border border-cyan-500/20 bg-cyan-500/5 px-4 py-1.5 text-xs font-medium text-cyan-400'
className='landing-animate-fade-up mb-8 inline-flex items-center gap-2 rounded-full border border-sky-500/20 bg-sky-500/5 px-4 py-1.5 text-xs font-medium text-sky-600 dark:border-sky-400/20 dark:bg-sky-400/5 dark:text-sky-400'
style={{ animationDelay: '0ms' }}
>
<span className='relative flex size-1.5'>
@@ -96,19 +95,19 @@ export function Hero(props: HeroProps) {
{/* Title */}
<h1
className='landing-animate-fade-up text-[clamp(2rem,5vw,3.5rem)] leading-[1.1] font-bold tracking-tight text-white'
className='landing-animate-fade-up text-[clamp(2rem,5vw,3.5rem)] leading-[1.1] font-bold tracking-tight text-foreground'
style={{ animationDelay: '60ms' }}
>
{t('LLM')}
<br />
<span className='bg-gradient-to-r from-cyan-400 via-blue-400 to-emerald-400 bg-clip-text text-transparent'>
<span className='bg-gradient-to-r from-sky-500 via-blue-500 to-teal-500 bg-clip-text text-transparent dark:from-sky-400 dark:via-blue-400 dark:to-teal-400'>
{t('Unified API Gateway')}
</span>
</h1>
{/* Subtitle */}
<p
className='landing-animate-fade-up mt-5 max-w-lg text-base leading-relaxed text-slate-400'
className='landing-animate-fade-up mt-5 max-w-lg text-base leading-relaxed text-muted-foreground'
style={{ animationDelay: '120ms' }}
>
{t(
@@ -118,20 +117,20 @@ export function Hero(props: HeroProps) {
{/* Base URL bar */}
<div
className='landing-animate-fade-up mt-8 flex w-full max-w-md items-center overflow-hidden rounded-xl border border-white/[0.08] bg-white/[0.03]'
className='landing-animate-fade-up mt-8 flex w-full max-w-md items-center overflow-hidden rounded-xl border border-border bg-muted/50 dark:bg-muted/30'
style={{ animationDelay: '180ms' }}
>
<div className='flex-1 overflow-hidden px-4 py-3 font-mono text-sm text-slate-300'>
<span className='text-emerald-400'>$</span>{' '}
<span className='text-slate-500'>curl</span>{' '}
<span className='text-cyan-400'>{serverAddress}</span>
<div className='flex-1 overflow-hidden px-4 py-3 font-mono text-sm text-foreground'>
<span className='text-emerald-600 dark:text-emerald-400'>$</span>{' '}
<span className='text-muted-foreground'>curl</span>{' '}
<span className='text-sky-600 dark:text-sky-400'>{serverAddress}</span>
</div>
<button
onClick={handleCopy}
className='flex h-full items-center gap-1.5 border-l border-white/[0.08] bg-white/[0.04] px-4 py-3 text-xs font-medium text-slate-400 transition-colors hover:bg-white/[0.08] hover:text-white'
className='flex h-full items-center gap-1.5 border-l border-border bg-muted/50 px-4 py-3 text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground dark:bg-muted/30 dark:hover:bg-muted/60'
>
{copied ? (
<Check className='size-3.5 text-emerald-400' />
<Check className='size-3.5 text-emerald-600 dark:text-emerald-400' />
) : (
<Copy className='size-3.5' />
)}
@@ -146,7 +145,7 @@ export function Hero(props: HeroProps) {
>
{props.isAuthenticated ? (
<Button
className='group h-11 rounded-lg border-0 bg-cyan-500 px-6 text-sm font-medium text-white hover:bg-cyan-400'
className='group h-11 rounded-lg bg-sky-600 px-6 text-sm font-medium text-white hover:bg-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400'
render={<Link to='/dashboard' />}
>
{t('Go to Dashboard')}
@@ -155,7 +154,7 @@ export function Hero(props: HeroProps) {
) : (
<>
<Button
className='group h-11 rounded-lg border-0 bg-cyan-500 px-6 text-sm font-medium text-white hover:bg-cyan-400'
className='group h-11 rounded-lg bg-sky-600 px-6 text-sm font-medium text-white hover:bg-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400'
render={<Link to='/sign-up' />}
>
{t('Get Started')}
@@ -163,7 +162,7 @@ export function Hero(props: HeroProps) {
</Button>
<Button
variant='outline'
className='h-11 rounded-lg border-white/[0.1] bg-white/[0.04] px-6 text-sm font-medium text-slate-300 hover:border-white/[0.2] hover:bg-white/[0.08] hover:text-white'
className='h-11 rounded-lg px-6 text-sm font-medium'
render={<Link to='/pricing' />}
>
{t('View Pricing')}
@@ -174,7 +173,7 @@ export function Hero(props: HeroProps) {
{/* Metrics */}
<div
className='landing-animate-fade-up mt-16 grid w-full max-w-lg grid-cols-4 overflow-hidden rounded-xl border border-white/[0.08] bg-white/[0.02]'
className='landing-animate-fade-up mt-16 grid w-full max-w-lg grid-cols-4 overflow-hidden rounded-xl border border-border bg-card'
style={{ animationDelay: '300ms' }}
>
{[
@@ -186,31 +185,31 @@ export function Hero(props: HeroProps) {
<div
key={m.label}
className={`flex flex-col items-center px-3 py-4 ${
i < 3 ? 'border-r border-white/[0.06]' : ''
i < 3 ? 'border-r border-border' : ''
}`}
>
<div className='text-lg font-bold tabular-nums text-white md:text-xl'>
<div className='text-lg font-bold tabular-nums text-foreground md:text-xl'>
{m.value}
<span className='text-sm font-medium text-cyan-400'>
<span className='text-sm font-medium text-sky-600 dark:text-sky-400'>
{m.suffix}
</span>
</div>
<div className='mt-1 text-[10px] font-medium tracking-widest text-slate-500 uppercase'>
<div className='mt-1 text-[10px] font-medium tracking-widest text-muted-foreground uppercase'>
{m.label}
</div>
</div>
))}
</div>
{/* Provider icons */}
{/* Provider tags */}
<div
className='landing-animate-fade-up mt-12 flex flex-col items-center gap-3'
style={{ animationDelay: '360ms' }}
>
<span className='text-[10px] font-medium tracking-[0.2em] text-slate-600 uppercase'>
<span className='text-[10px] font-medium tracking-[0.2em] text-muted-foreground uppercase'>
{t('Supported Providers')}
</span>
<div className='flex flex-wrap items-center justify-center gap-4'>
<div className='flex flex-wrap items-center justify-center gap-2'>
{[
'OpenAI',
'Claude',
@@ -233,12 +232,12 @@ export function Hero(props: HeroProps) {
].map((name) => (
<span
key={name}
className='rounded-md border border-white/[0.06] bg-white/[0.02] px-2.5 py-1 text-[11px] font-medium text-slate-500 transition-colors hover:border-cyan-500/20 hover:text-cyan-400'
className='rounded-md border border-border bg-muted/50 px-2.5 py-1 text-[11px] font-medium text-muted-foreground transition-colors hover:border-sky-500/30 hover:text-sky-600 dark:bg-muted/30 dark:hover:border-sky-400/30 dark:hover:text-sky-400'
>
{name}
</span>
))}
<span className='px-2.5 py-1 text-[11px] font-semibold text-cyan-400'>
<span className='px-2.5 py-1 text-[11px] font-semibold text-sky-600 dark:text-sky-400'>
30+
</span>
</div>
@@ -49,13 +49,13 @@ export function HowItWorks() {
]
return (
<section className='relative z-10 border-t border-white/[0.06] bg-[#0d1117] px-6 py-24 md:py-32'>
<section className='relative z-10 border-t border-border bg-muted/30 px-6 py-24 md:py-32'>
<div className='mx-auto max-w-5xl'>
<AnimateInView className='mb-16 text-center md:mb-20'>
<p className='mb-3 text-xs font-medium tracking-widest text-cyan-400/60 uppercase'>
<p className='mb-3 text-xs font-medium tracking-widest text-sky-600/60 uppercase dark:text-sky-400/60'>
{t('How It Works')}
</p>
<h2 className='text-2xl font-bold tracking-tight text-white md:text-3xl'>
<h2 className='text-2xl font-bold tracking-tight text-foreground md:text-3xl'>
{t('Three steps to get started')}
</h2>
</AnimateInView>
@@ -69,17 +69,17 @@ export function HowItWorks() {
className='relative flex flex-col items-center text-center'
>
<div className='relative mb-6'>
<div className='flex size-16 items-center justify-center rounded-2xl border border-white/[0.06] bg-white/[0.03] text-slate-400'>
<div className='flex size-16 items-center justify-center rounded-2xl border border-border bg-card text-muted-foreground'>
{step.icon}
</div>
<div className='absolute -top-2 -right-2 flex size-6 items-center justify-center rounded-full bg-cyan-500 text-xs font-bold text-white'>
<div className='absolute -top-2 -right-2 flex size-6 items-center justify-center rounded-full bg-sky-600 text-xs font-bold text-white dark:bg-sky-500'>
{step.num}
</div>
</div>
<h3 className='mb-2 text-base font-semibold text-white'>
<h3 className='mb-2 text-base font-semibold text-foreground'>
{step.title}
</h3>
<p className='max-w-[240px] text-sm leading-relaxed text-slate-500'>
<p className='max-w-[240px] text-sm leading-relaxed text-muted-foreground'>
{step.desc}
</p>
</AnimateInView>
@@ -105,20 +105,20 @@ export function Stats(_props: StatsProps) {
]
return (
<div className='relative z-10 border-y border-white/[0.06] bg-[#0d1117]'>
<div className='relative z-10 border-y border-border bg-muted/30'>
<div className='mx-auto max-w-5xl px-6 py-10 md:py-12'>
<div className='grid grid-cols-2 gap-8 md:grid-cols-4 md:gap-12'>
{stats.map((s, i) => (
<div
key={s.label}
className={`flex flex-col items-center text-center ${
i < 3 ? 'md:border-r md:border-white/[0.06]' : ''
i < 3 ? 'md:border-r md:border-border' : ''
}`}
>
<span className='text-2xl font-bold tracking-tight text-white md:text-3xl'>
<span className='text-2xl font-bold tracking-tight text-foreground md:text-3xl'>
<Counter end={s.end} suffix={s.suffix} decimals={s.decimals} />
</span>
<span className='mt-1.5 text-xs text-slate-500'>
<span className='mt-1.5 text-xs text-muted-foreground'>
{s.label}
</span>
</div>