feat: complete editorial redesign of Wordly.art SaaS
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m31s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 1m31s
New design system with cream/dark/copper palette (#FDFCF9, #1A1A1A, #C5A17A), Inter font, rounded editorial cards, and dark mode support. - globals.css: Rewritten from 1867→200 lines, new brand colors + editorial utilities - Landing page: New editorial design with Hero, Steps, Features, Layout, Formats, Pricing, CTA, Footer - Auth pages: Editorial card design with decorative blur blobs and back-to-home links - Dashboard sidebar: New w-72 sidebar with Momento promo section - Dashboard header: New h-20 topbar with editorial styling - i18n: Added memento/common/dashboard keys for all 13 locales, cleaned duplicate landing keys Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,21 +1,24 @@
|
||||
'use client';
|
||||
|
||||
import { Suspense } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { LoginForm } from './LoginForm';
|
||||
import { Loader2, Languages } from 'lucide-react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
function LoadingFallback() {
|
||||
const { t } = useI18n();
|
||||
return (
|
||||
<div className="w-full max-w-md mx-auto">
|
||||
<div className="rounded-xl bg-card border border-border shadow-lg p-8">
|
||||
<div className="editorial-card p-8">
|
||||
<div className="flex flex-col items-center justify-center py-8 space-y-4">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary text-primary-foreground">
|
||||
<Languages className="h-6 w-6" />
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-brand-dark text-white">
|
||||
<span className="text-xl font-bold">W</span>
|
||||
</div>
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
<p className="text-muted-foreground">{t('common.loading')}</p>
|
||||
<Loader2 className="h-8 w-8 animate-spin text-brand-accent" />
|
||||
<p className="text-brand-dark/50 dark:text-white/50 text-sm">{t('common.loading')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -23,23 +26,43 @@ function LoadingFallback() {
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-surface via-surface-elevated to-background flex items-center justify-center p-4 relative overflow-hidden">
|
||||
<div className="absolute inset-0">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-accent/5" />
|
||||
<div className="absolute inset-0 bg-[url('/grid.svg')] opacity-5" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-background via-background/90 to-background/50" />
|
||||
</div>
|
||||
|
||||
<div className="min-h-screen bg-brand-bg dark:bg-[#0a0a0a] flex items-center justify-center p-4 relative overflow-hidden">
|
||||
{/* Decorative blur blobs */}
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div className="absolute top-20 left-10 w-32 h-32 bg-primary/5 rounded-full blur-3xl animate-pulse" />
|
||||
<div className="absolute bottom-20 right-20 w-24 h-24 bg-accent/5 rounded-full blur-2xl animate-pulse" />
|
||||
<div className="absolute top-1/2 right-1/4 w-16 h-16 bg-success/5 rounded-full blur-xl animate-pulse" />
|
||||
<div className="absolute top-1/4 left-1/4 w-[500px] h-[500px] bg-brand-accent/10 rounded-full blur-3xl" />
|
||||
<div className="absolute bottom-1/4 right-1/4 w-[400px] h-[400px] bg-brand-dark/5 dark:bg-brand-accent/5 rounded-full blur-3xl" />
|
||||
</div>
|
||||
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
{/* Back to home */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||
className="absolute top-6 left-6 z-20"
|
||||
>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center gap-2 text-brand-dark/50 dark:text-white/50 hover:text-brand-dark dark:hover:text-white transition-colors text-[11px] font-bold uppercase tracking-widest"
|
||||
>
|
||||
<ChevronLeft size={16} />
|
||||
{t('common.backToHome')}
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
||||
{/* Form container */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, ease: 'easeOut', delay: 0.1 }}
|
||||
className="relative z-10 w-full max-w-md"
|
||||
>
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<LoginForm />
|
||||
</Suspense>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
'use client';
|
||||
|
||||
import { Languages, Loader2 } from 'lucide-react';
|
||||
import { Suspense } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { RegisterForm } from './RegisterForm';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { ChevronLeft } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
function LoadingFallback() {
|
||||
const { t } = useI18n();
|
||||
return (
|
||||
<div className="w-full max-w-md mx-auto">
|
||||
<div className="rounded-xl bg-card border border-border shadow-lg p-8">
|
||||
<div className="editorial-card p-8">
|
||||
<div className="flex flex-col items-center justify-center py-8 space-y-4">
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-primary text-primary-foreground">
|
||||
<Languages className="h-6 w-6" />
|
||||
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-brand-dark text-white">
|
||||
<span className="text-xl font-bold">W</span>
|
||||
</div>
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
<p className="text-muted-foreground">{t('common.loading')}</p>
|
||||
<Loader2 className="h-8 w-8 animate-spin text-brand-accent" />
|
||||
<p className="text-brand-dark/50 dark:text-white/50 text-sm">{t('common.loading')}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -22,26 +26,43 @@ function LoadingFallback() {
|
||||
}
|
||||
|
||||
export default function RegisterPage() {
|
||||
const { t } = useI18n();
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-surface via-surface-elevated to-background flex items-center justify-center p-4 relative overflow-hidden">
|
||||
{/* Background gradient */}
|
||||
<div className="absolute inset-0">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-transparent to-accent/5" />
|
||||
<div className="absolute inset-0 bg-[url('/grid.svg')] opacity-5" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-background via-background/90 to-background/50" />
|
||||
</div>
|
||||
|
||||
{/* Decorative floating elements */}
|
||||
<div className="min-h-screen bg-brand-bg dark:bg-[#0a0a0a] flex items-center justify-center p-4 relative overflow-hidden">
|
||||
{/* Decorative blur blobs */}
|
||||
<div className="absolute inset-0 overflow-hidden pointer-events-none">
|
||||
<div className="absolute top-20 left-10 w-20 h-20 bg-primary/10 rounded-full blur-xl animate-pulse" />
|
||||
<div className="absolute top-40 right-20 w-32 h-32 bg-accent/10 rounded-full blur-2xl animate-pulse" />
|
||||
<div className="absolute bottom-20 left-1/4 w-16 h-16 bg-success/10 rounded-full blur-lg animate-pulse" />
|
||||
<div className="absolute top-1/4 left-1/4 w-[500px] h-[500px] bg-brand-accent/10 rounded-full blur-3xl" />
|
||||
<div className="absolute bottom-1/4 right-1/4 w-[400px] h-[400px] bg-brand-dark/5 dark:bg-brand-accent/5 rounded-full blur-3xl" />
|
||||
</div>
|
||||
|
||||
{/* Form — Suspense required by useSearchParams() in useRegister */}
|
||||
<div className="relative z-10 w-full max-w-md">
|
||||
<RegisterForm />
|
||||
</div>
|
||||
{/* Back to home */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -10 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.5, ease: 'easeOut' }}
|
||||
className="absolute top-6 left-6 z-20"
|
||||
>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center gap-2 text-brand-dark/50 dark:text-white/50 hover:text-brand-dark dark:hover:text-white transition-colors text-[11px] font-bold uppercase tracking-widest"
|
||||
>
|
||||
<ChevronLeft size={16} />
|
||||
{t('common.backToHome')}
|
||||
</Link>
|
||||
</motion.div>
|
||||
|
||||
{/* Form container */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.6, ease: 'easeOut', delay: 0.1 }}
|
||||
className="relative z-10 w-full max-w-md"
|
||||
>
|
||||
<Suspense fallback={<LoadingFallback />}>
|
||||
<RegisterForm />
|
||||
</Suspense>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user