fix(nav): add mobile hamburger menu to header to ensure parity with desktop
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 47s
Build & Deploy / 🧪 QA (push) Successful in 1m4s
Build & Deploy / 🏗️ Build (push) Successful in 1m54s
Build & Deploy / 🚀 Deploy (push) Successful in 31s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 43s
Build & Deploy / 🔔 Notify (push) Successful in 1s
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 47s
Build & Deploy / 🧪 QA (push) Successful in 1m4s
Build & Deploy / 🏗️ Build (push) Successful in 1m54s
Build & Deploy / 🚀 Deploy (push) Successful in 31s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 43s
Build & Deploy / 🔔 Notify (push) Successful in 1s
This commit is contained in:
@@ -22,6 +22,7 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
const [isScrolled, setIsScrolled] = React.useState(false);
|
||||
const [forceSolid, setForceSolid] = React.useState(false);
|
||||
const [headerVariant, setHeaderVariant] = React.useState<'capsule' | 'standard'>('capsule');
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = React.useState(false);
|
||||
const pathname = usePathname() || '/';
|
||||
|
||||
// Scroll bound shine effect (continuous looping as user scrolls)
|
||||
@@ -55,6 +56,7 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
|
||||
// Re-check when pathname changes
|
||||
checkHeaderState();
|
||||
setIsMobileMenuOpen(false);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll);
|
||||
@@ -154,10 +156,82 @@ export function Header({ navLinks }: HeaderProps) {
|
||||
{/* Sleek shine sweep effect */}
|
||||
<span className="absolute top-0 -left-[150%] h-[200%] w-[150%] bg-gradient-to-r from-transparent via-white/30 to-transparent skew-x-[-20deg] group-hover/cta:left-[100%] transition-all duration-1000 ease-[cubic-bezier(0.16,1,0.3,1)] z-30 pointer-events-none" />
|
||||
</TransitionLink>
|
||||
|
||||
{/* Hamburger Menu (Mobile Only) */}
|
||||
<button
|
||||
className={`md:hidden ml-2 p-2 transition-colors focus:outline-none ${isSolidMode && !isMobileMenuOpen ? 'text-text-primary hover:text-primary' : 'text-white hover:text-primary z-[60] relative'}`}
|
||||
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
|
||||
aria-label="Toggle Menu"
|
||||
>
|
||||
<div className="w-6 h-5 flex flex-col justify-between relative">
|
||||
<span className={`w-full h-0.5 bg-current transform transition duration-300 ease-in-out ${isMobileMenuOpen ? 'rotate-45 translate-y-2' : ''}`} />
|
||||
<span className={`w-full h-0.5 bg-current transition duration-300 ease-in-out ${isMobileMenuOpen ? 'opacity-0' : 'opacity-100'}`} />
|
||||
<span className={`w-full h-0.5 bg-current transform transition duration-300 ease-in-out ${isMobileMenuOpen ? '-rotate-45 -translate-y-2.5' : ''}`} />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Mobile Menu Overlay */}
|
||||
<AnimatePresence>
|
||||
{isMobileMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="fixed inset-0 z-[40] bg-[#050B14]/95 backdrop-blur-xl md:hidden pt-24 pb-24 px-6 overflow-y-auto"
|
||||
>
|
||||
<nav className="flex flex-col gap-6 mt-8">
|
||||
{navLinks && navLinks.length > 0 && navLinks.map((link) => {
|
||||
const mappedUrl = link.url.startsWith('/') && !link.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${link.url}`
|
||||
: link.url;
|
||||
const isActive = pathname === mappedUrl || (mappedUrl !== `/${currentLocale}` && pathname?.startsWith(`${mappedUrl}/`));
|
||||
|
||||
return (
|
||||
<div key={link.url} className="flex flex-col">
|
||||
<TransitionLink
|
||||
href={mappedUrl}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={`text-2xl font-bold uppercase tracking-widest transition-colors ${isActive ? 'text-primary' : 'text-white'}`}
|
||||
>
|
||||
{link.label}
|
||||
</TransitionLink>
|
||||
|
||||
{link.children && (
|
||||
<div className="flex flex-col gap-3 mt-3 pl-4 border-l-2 border-white/10">
|
||||
{link.children.map((child, idx) => {
|
||||
if (child.isGroupLabel) {
|
||||
return <div key={idx} className="text-sm font-bold text-neutral-500 uppercase tracking-widest mt-2">{child.label}</div>;
|
||||
}
|
||||
const childUrl = child.url.startsWith('/') && !child.url.match(/^\/(en|de)/)
|
||||
? `/${currentLocale}${child.url}`
|
||||
: child.url;
|
||||
const isChildActive = pathname === childUrl;
|
||||
|
||||
return (
|
||||
<TransitionLink
|
||||
key={child.url}
|
||||
href={childUrl}
|
||||
onClick={() => setIsMobileMenuOpen(false)}
|
||||
className={`text-lg font-medium transition-colors ${isChildActive ? 'text-primary' : 'text-white/70'}`}
|
||||
>
|
||||
{child.label}
|
||||
</TransitionLink>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user