109 lines
4.9 KiB
TypeScript
109 lines
4.9 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { NAV_ITEMS } from '../constants';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { Link } from 'react-router-dom';
|
|
|
|
const Header: React.FC = () => {
|
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
const [scrolled, setScrolled] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const handleScroll = () => {
|
|
setScrolled(window.scrollY > 50);
|
|
};
|
|
window.addEventListener('scroll', handleScroll);
|
|
return () => window.removeEventListener('scroll', handleScroll);
|
|
}, []);
|
|
|
|
return (
|
|
<header
|
|
className={`fixed top-0 w-full z-50 transition-all duration-500 ${scrolled
|
|
? 'bg-white/80 dark:bg-black/80 backdrop-blur-xl py-2 border-b border-stone-200/50 dark:border-stone-800/50'
|
|
: 'bg-transparent py-6'
|
|
}`}
|
|
>
|
|
<div className="max-w-[1920px] mx-auto px-6 md:px-12">
|
|
<div className="flex justify-between items-center h-20">
|
|
{/* Mobile Menu Button */}
|
|
<div className="flex items-center md:hidden">
|
|
<button
|
|
className="text-text-main dark:text-white p-2 hover:bg-stone-100 dark:hover:bg-stone-800 rounded-full transition-colors"
|
|
type="button"
|
|
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
|
>
|
|
<span className="material-symbols-outlined">menu</span>
|
|
</button>
|
|
</div>
|
|
|
|
{/* Logo */}
|
|
<div className="flex-shrink-0 relative group cursor-pointer">
|
|
<Link className="font-display text-4xl md:text-5xl font-light tracking-widest uppercase text-text-main dark:text-white" to="/">
|
|
HOTCHPOTSH
|
|
</Link>
|
|
{/* Subtle glow effect on hover */}
|
|
<div className="absolute -inset-4 bg-white/20 blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500 rounded-full" />
|
|
</div>
|
|
|
|
{/* Desktop Nav */}
|
|
<nav className="hidden md:flex space-x-12">
|
|
{NAV_ITEMS.map((item) => (
|
|
<Link
|
|
key={item.label}
|
|
className="group relative text-xs uppercase tracking-[0.2em] text-text-main dark:text-white hover:text-black dark:hover:text-white transition-colors duration-300 py-2"
|
|
to={item.label === 'Collections' ? '/collections' : item.label === 'Atelier' ? '/atelier' : '/editorial'}
|
|
>
|
|
{item.label}
|
|
{/* Underline Reveal Animation */}
|
|
<span className="absolute bottom-0 left-0 w-0 h-[1px] bg-black dark:bg-white transition-all duration-300 group-hover:w-full" />
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
|
|
{/* Icons */}
|
|
<div className="flex items-center space-x-6 text-text-main dark:text-white">
|
|
<button className="hover:scale-110 transition-transform duration-300 hidden sm:block p-2">
|
|
<span className="material-symbols-outlined text-xl font-light">search</span>
|
|
</button>
|
|
<a className="hover:scale-110 transition-transform duration-300 relative group p-2" href="#">
|
|
<span className="material-symbols-outlined text-xl font-light">shopping_bag</span>
|
|
<span className="absolute top-0 right-0 bg-black dark:bg-white text-white dark:text-black text-[9px] w-4 h-4 flex items-center justify-center rounded-full opacity-0 scale-50 group-hover:opacity-100 group-hover:scale-100 transition-all duration-300">2</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Mobile Menu Overlay */}
|
|
<AnimatePresence>
|
|
{isMenuOpen && (
|
|
<motion.div
|
|
initial={{ opacity: 0, height: 0 }}
|
|
animate={{ opacity: 1, height: 'auto' }}
|
|
exit={{ opacity: 0, height: 0 }}
|
|
className="md:hidden absolute top-full left-0 w-full bg-white/95 dark:bg-black/95 backdrop-blur-xl border-b border-stone-200 dark:border-stone-800 shadow-2xl overflow-hidden"
|
|
>
|
|
<div className="flex flex-col p-8 space-y-6">
|
|
{NAV_ITEMS.map((item, idx) => (
|
|
<Link
|
|
key={item.label}
|
|
to={item.label === 'Collections' ? '/collections' : item.label === 'Atelier' ? '/atelier' : '/editorial'}
|
|
className="text-lg uppercase tracking-[0.2em] text-text-main dark:text-white hover:pl-4 transition-all duration-300 border-l-2 border-transparent hover:border-black dark:hover:border-white"
|
|
onClick={() => setIsMenuOpen(false)}
|
|
>
|
|
<motion.span
|
|
initial={{ x: -20, opacity: 0 }}
|
|
animate={{ x: 0, opacity: 1 }}
|
|
transition={{ delay: idx * 0.1 }}
|
|
>
|
|
{item.label}
|
|
</motion.span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</header>
|
|
);
|
|
};
|
|
|
|
export default Header; |