fix
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
import { motion } from 'framer-motion';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
import { Button } from './ui';
|
import { Button } from './ui';
|
||||||
@@ -70,79 +71,169 @@ export default function Header() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className={headerClass}>
|
<motion.header
|
||||||
|
className={headerClass}
|
||||||
|
initial={{ y: -100, opacity: 0 }}
|
||||||
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
transition={{ duration: 0.8, ease: "easeOut" }}
|
||||||
|
>
|
||||||
<div className="container mx-auto px-4 md:px-12 lg:px-16 max-w-7xl flex items-center justify-between">
|
<div className="container mx-auto px-4 md:px-12 lg:px-16 max-w-7xl flex items-center justify-between">
|
||||||
<Link href={`/${currentLocale}`} className="flex-shrink-0 group touch-target">
|
<motion.div
|
||||||
<Image
|
className="flex-shrink-0 group touch-target"
|
||||||
src={logoSrc}
|
initial={{ scale: 0.8, opacity: 0 }}
|
||||||
alt={t('home')}
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
width={120}
|
transition={{ duration: 0.6, ease: "easeOut", delay: 0.1 }}
|
||||||
height={120}
|
>
|
||||||
className="h-10 md:h-14 w-auto transition-all duration-500 group-hover:scale-110"
|
<Link href={`/${currentLocale}`}>
|
||||||
priority
|
<Image
|
||||||
unoptimized
|
src={logoSrc}
|
||||||
/>
|
alt={t('home')}
|
||||||
</Link>
|
width={120}
|
||||||
|
height={120}
|
||||||
|
className="h-10 md:h-14 w-auto transition-all duration-500 group-hover:scale-110"
|
||||||
|
priority
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
<div className="flex items-center gap-4 md:gap-12">
|
<motion.div
|
||||||
<nav className="hidden lg:flex items-center space-x-10">
|
className="flex items-center gap-4 md:gap-12"
|
||||||
{menuItems.map((item) => (
|
initial="hidden"
|
||||||
<Link
|
animate="visible"
|
||||||
key={item.href}
|
variants={{
|
||||||
href={`/${currentLocale}${item.href === '/' ? '' : item.href}`}
|
visible: {
|
||||||
className={cn(
|
transition: {
|
||||||
textColorClass,
|
staggerChildren: 0.08,
|
||||||
"hover:text-accent font-bold transition-all text-base md:text-lg tracking-tight relative group"
|
delayChildren: 0.3
|
||||||
)}
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<motion.nav
|
||||||
|
className="hidden lg:flex items-center space-x-10"
|
||||||
|
variants={navVariants}
|
||||||
|
>
|
||||||
|
{menuItems.map((item, idx) => (
|
||||||
|
<motion.div
|
||||||
|
key={item.href}
|
||||||
|
variants={navLinkVariants}
|
||||||
|
custom={idx}
|
||||||
>
|
>
|
||||||
{item.label}
|
<Link
|
||||||
<span className="absolute -bottom-2 left-0 w-0 h-1 bg-accent transition-all duration-300 group-hover:w-full rounded-full" />
|
href={`/${currentLocale}${item.href === '/' ? '' : item.href}`}
|
||||||
</Link>
|
className={cn(
|
||||||
|
textColorClass,
|
||||||
|
"hover:text-accent font-bold transition-all text-base md:text-lg tracking-tight relative group inline-block"
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
<span className="absolute -bottom-2 left-0 w-0 h-1 bg-accent transition-all duration-300 group-hover:w-full rounded-full" />
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</nav>
|
</motion.nav>
|
||||||
|
|
||||||
<div className={cn("hidden lg:flex items-center space-x-8", textColorClass)}>
|
<motion.div
|
||||||
<div className="flex items-center space-x-4 text-xs md:text-sm font-extrabold tracking-widest uppercase">
|
className={cn("hidden lg:flex items-center space-x-8", textColorClass)}
|
||||||
<Link
|
variants={headerRightVariants}
|
||||||
href={getPathForLocale('en')}
|
>
|
||||||
className={`hover:text-accent transition-colors flex items-center gap-2 touch-target ${currentLocale === 'en' ? 'text-accent' : 'opacity-60'}`}
|
<motion.div
|
||||||
>
|
className="flex items-center space-x-4 text-xs md:text-sm font-extrabold tracking-widest uppercase"
|
||||||
EN
|
initial={{ opacity: 0, x: 20 }}
|
||||||
</Link>
|
animate={{ opacity: 1, x: 0 }}
|
||||||
<div className="w-px h-4 bg-current opacity-20" />
|
transition={{ duration: 0.5, delay: 0.6 }}
|
||||||
<Link
|
|
||||||
href={getPathForLocale('de')}
|
|
||||||
className={`hover:text-accent transition-colors flex items-center gap-2 touch-target ${currentLocale === 'de' ? 'text-accent' : 'opacity-60'}`}
|
|
||||||
>
|
|
||||||
DE
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
href={`/${currentLocale}/contact`}
|
|
||||||
variant="white"
|
|
||||||
size="md"
|
|
||||||
className="px-8 shadow-xl"
|
|
||||||
>
|
>
|
||||||
{t('contact')}
|
<motion.div
|
||||||
</Button>
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
</div>
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{ duration: 0.4, delay: 0.65 }}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href={getPathForLocale('en')}
|
||||||
|
className={`hover:text-accent transition-colors flex items-center gap-2 touch-target ${currentLocale === 'en' ? 'text-accent' : 'opacity-60'}`}
|
||||||
|
>
|
||||||
|
EN
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
className="w-px h-4 bg-current opacity-20"
|
||||||
|
initial={{ scaleY: 0 }}
|
||||||
|
animate={{ scaleY: 1 }}
|
||||||
|
transition={{ duration: 0.4, delay: 0.7 }}
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
|
transition={{ duration: 0.4, delay: 0.75 }}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href={getPathForLocale('de')}
|
||||||
|
className={`hover:text-accent transition-colors flex items-center gap-2 touch-target ${currentLocale === 'de' ? 'text-accent' : 'opacity-60'}`}
|
||||||
|
>
|
||||||
|
DE
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
initial={{ scale: 0.9, opacity: 0 }}
|
||||||
|
animate={{ scale: 1, opacity: 1 }}
|
||||||
|
transition={{ duration: 0.6, type: "spring", stiffness: 400, delay: 0.7 }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
href={`/${currentLocale}/contact`}
|
||||||
|
variant="white"
|
||||||
|
size="md"
|
||||||
|
className="px-8 shadow-xl"
|
||||||
|
>
|
||||||
|
{t('contact')}
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
{/* Mobile Menu Button */}
|
{/* Mobile Menu Button */}
|
||||||
<button
|
<motion.button
|
||||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
|
||||||
className={cn("lg:hidden touch-target p-2 rounded-xl bg-white/10 border border-white/20 z-50", textColorClass)}
|
className={cn("lg:hidden touch-target p-2 rounded-xl bg-white/10 border border-white/20 z-50", textColorClass)}
|
||||||
aria-label={t('toggleMenu')}
|
aria-label={t('toggleMenu')}
|
||||||
|
initial={{ scale: 0.8, opacity: 0, rotate: -180 }}
|
||||||
|
animate={{ scale: 1, opacity: 1, rotate: 0 }}
|
||||||
|
transition={{ duration: 0.6, type: "spring", stiffness: 300, damping: 20, delay: 0.5 }}
|
||||||
|
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||||
>
|
>
|
||||||
<svg className="w-7 h-7" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<motion.svg
|
||||||
|
className="w-7 h-7"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
initial={{ scale: 0 }}
|
||||||
|
animate={{ scale: 1 }}
|
||||||
|
transition={{ duration: 0.3, delay: 0.6 }}
|
||||||
|
>
|
||||||
{isMobileMenuOpen ? (
|
{isMobileMenuOpen ? (
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
<motion.path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M6 18L18 6M6 6l12 12"
|
||||||
|
initial={{ pathLength: 0 }}
|
||||||
|
animate={{ pathLength: 1 }}
|
||||||
|
transition={{ duration: 0.4, delay: 0.7 }}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
|
<motion.path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M4 6h16M4 12h16M4 18h16"
|
||||||
|
initial={{ pathLength: 0 }}
|
||||||
|
animate={{ pathLength: 1 }}
|
||||||
|
transition={{ duration: 0.4, delay: 0.7 }}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</svg>
|
</motion.svg>
|
||||||
</button>
|
</motion.button>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Mobile Menu Overlay */}
|
{/* Mobile Menu Overlay */}
|
||||||
@@ -150,58 +241,157 @@ export default function Header() {
|
|||||||
"fixed inset-0 bg-primary z-40 lg:hidden transition-all duration-500 ease-in-out flex flex-col",
|
"fixed inset-0 bg-primary z-40 lg:hidden transition-all duration-500 ease-in-out flex flex-col",
|
||||||
isMobileMenuOpen ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-full pointer-events-none"
|
isMobileMenuOpen ? "opacity-100 translate-y-0" : "opacity-0 -translate-y-full pointer-events-none"
|
||||||
)}>
|
)}>
|
||||||
<div className="flex-grow flex flex-col justify-center items-center p-8 space-y-8">
|
<motion.div
|
||||||
|
className="flex-grow flex flex-col justify-center items-center p-8 space-y-8"
|
||||||
|
initial="closed"
|
||||||
|
animate={isMobileMenuOpen ? "open" : "closed"}
|
||||||
|
variants={{
|
||||||
|
open: {
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.1,
|
||||||
|
delayChildren: 0.2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
{menuItems.map((item, idx) => (
|
{menuItems.map((item, idx) => (
|
||||||
<Link
|
<motion.div
|
||||||
key={item.href}
|
key={item.href}
|
||||||
href={`/${currentLocale}${item.href === '/' ? '' : item.href}`}
|
variants={{
|
||||||
className={cn(
|
closed: { opacity: 0, y: 50, scale: 0.9 },
|
||||||
"text-2xl md:text-3xl font-extrabold text-white hover:text-accent transition-all transform",
|
open: {
|
||||||
isMobileMenuOpen ? "translate-y-0 opacity-100" : "translate-y-10 opacity-0"
|
opacity: 1,
|
||||||
)}
|
y: 0,
|
||||||
style={{ transitionDelay: `${idx * 100}ms` }}
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.6,
|
||||||
|
ease: "easeOut",
|
||||||
|
delay: idx * 0.08
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{item.label}
|
<Link
|
||||||
</Link>
|
href={`/${currentLocale}${item.href === '/' ? '' : item.href}`}
|
||||||
|
className="text-2xl md:text-3xl font-extrabold text-white hover:text-accent transition-all transform block py-4"
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className={cn(
|
<motion.div
|
||||||
"pt-8 border-t border-white/10 w-full flex flex-col items-center space-y-8 transition-all duration-500 delay-500",
|
className="pt-8 border-t border-white/10 w-full flex flex-col items-center space-y-8"
|
||||||
isMobileMenuOpen ? "translate-y-0 opacity-100" : "translate-y-10 opacity-0"
|
initial={{ opacity: 0, y: 30 }}
|
||||||
)}>
|
animate={isMobileMenuOpen ? { opacity: 1, y: 0 } : { opacity: 0, y: 30 }}
|
||||||
<div className="flex items-center space-x-8 text-lg md:text-xl font-extrabold tracking-widest uppercase text-white">
|
transition={{ duration: 0.5, delay: 0.8 }}
|
||||||
<Link
|
>
|
||||||
href={getPathForLocale('en')}
|
<motion.div
|
||||||
className={`hover:text-accent transition-colors ${currentLocale === 'en' ? 'text-accent' : 'opacity-60'}`}
|
className="flex items-center space-x-8 text-lg md:text-xl font-extrabold tracking-widest uppercase text-white"
|
||||||
>
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
EN
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
</Link>
|
transition={{ duration: 0.4, delay: 0.9 }}
|
||||||
<div className="w-px h-6 bg-white/20" />
|
|
||||||
<Link
|
|
||||||
href={getPathForLocale('de')}
|
|
||||||
className={`hover:text-accent transition-colors ${currentLocale === 'de' ? 'text-accent' : 'opacity-60'}`}
|
|
||||||
>
|
|
||||||
DE
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
href={`/${currentLocale}/contact`}
|
|
||||||
variant="accent"
|
|
||||||
size="lg"
|
|
||||||
className="w-full max-w-xs py-6 text-lg md:text-xl shadow-2xl"
|
|
||||||
>
|
>
|
||||||
{t('contact')}
|
<motion.div
|
||||||
</Button>
|
initial={{ opacity: 0 }}
|
||||||
</div>
|
animate={{ opacity: 1 }}
|
||||||
</div>
|
transition={{ duration: 0.3, delay: 1.0 }}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href={getPathForLocale('en')}
|
||||||
|
className={`hover:text-accent transition-colors ${currentLocale === 'en' ? 'text-accent' : 'opacity-60'}`}
|
||||||
|
>
|
||||||
|
EN
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div
|
||||||
|
className="w-px h-6 bg-white/20"
|
||||||
|
initial={{ scaleX: 0 }}
|
||||||
|
animate={{ scaleX: 1 }}
|
||||||
|
transition={{ duration: 0.4, delay: 1.05 }}
|
||||||
|
/>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 0.3, delay: 1.1 }}
|
||||||
|
>
|
||||||
|
<Link
|
||||||
|
href={getPathForLocale('de')}
|
||||||
|
className={`hover:text-accent transition-colors ${currentLocale === 'de' ? 'text-accent' : 'opacity-60'}`}
|
||||||
|
>
|
||||||
|
DE
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
|
<motion.div
|
||||||
|
initial={{ scale: 0.9, opacity: 0, y: 20 }}
|
||||||
|
animate={{ scale: 1, opacity: 1, y: 0 }}
|
||||||
|
transition={{ type: "spring", stiffness: 400, damping: 20, delay: 1.2 }}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
href={`/${currentLocale}/contact`}
|
||||||
|
variant="accent"
|
||||||
|
size="lg"
|
||||||
|
className="w-full max-w-xs py-6 text-lg md:text-xl shadow-2xl"
|
||||||
|
>
|
||||||
|
{t('contact')}
|
||||||
|
</Button>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
{/* Bottom Branding */}
|
{/* Bottom Branding */}
|
||||||
<div className="p-12 flex justify-center opacity-20">
|
<motion.div
|
||||||
<Image src="/logo-white.svg" alt={t('home')} width={80} height={80} unoptimized />
|
className="p-12 flex justify-center opacity-20"
|
||||||
</div>
|
initial={{ opacity: 0, scale: 0.8 }}
|
||||||
|
animate={isMobileMenuOpen ? { opacity: 0.2, scale: 1 } : { opacity: 0, scale: 0.8 }}
|
||||||
|
transition={{ duration: 0.5, delay: 1.4 }}
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
initial={{ scale: 0.5 }}
|
||||||
|
animate={{ scale: 1 }}
|
||||||
|
transition={{ type: "spring", stiffness: 300, delay: 1.5 }}
|
||||||
|
>
|
||||||
|
<Image src="/logo-white.svg" alt={t('home')} width={80} height={80} unoptimized />
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</motion.header>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const navVariants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.06,
|
||||||
|
delayChildren: 0.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const navLinkVariants = {
|
||||||
|
hidden: { opacity: 0, y: 20, scale: 0.9 },
|
||||||
|
visible: (idx: number) => ({
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
scale: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.5,
|
||||||
|
ease: [0.25, 0.46, 0.45, 0.94],
|
||||||
|
delay: idx * 0.06
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const headerRightVariants = {
|
||||||
|
hidden: { opacity: 0, x: 30 },
|
||||||
|
visible: {
|
||||||
|
opacity: 1,
|
||||||
|
x: 0,
|
||||||
|
transition: { duration: 0.6, ease: "easeOut" }
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user