feat: complete editorial redesign of Wordly.art SaaS
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:
2026-05-16 12:03:49 +02:00
parent 18e1aca148
commit b2b6f03399
10 changed files with 2583 additions and 4180 deletions

View File

@@ -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>
);
}

View File

@@ -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>
);
}