Import source: src/components/Navbar/Navbar
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
import { useState, useEffect } from 'react'
|
||||||
|
import { motion, AnimatePresence } from 'framer-motion'
|
||||||
|
|
||||||
|
const navLinks = [
|
||||||
|
{ label: 'Discover', href: '#discover' },
|
||||||
|
{ label: 'Artists', href: '#artists' },
|
||||||
|
{ label: 'Albums', href: '#albums' },
|
||||||
|
{ label: 'Charts', href: '#charts' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function Navbar() {
|
||||||
|
const [scrolled, setScrolled] = useState(false)
|
||||||
|
const [menuOpen, setMenuOpen] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = () => setScrolled(window.scrollY > 40)
|
||||||
|
window.addEventListener('scroll', handler, { passive: true })
|
||||||
|
return () => window.removeEventListener('scroll', handler)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<motion.nav
|
||||||
|
initial={{ y: -72, opacity: 0 }}
|
||||||
|
animate={{ y: 0, opacity: 1 }}
|
||||||
|
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }}
|
||||||
|
className={`fixed top-0 left-0 right-0 z-50 transition-all duration-500 ${
|
||||||
|
scrolled ? 'glass-dark border-b border-white/5 shadow-card' : 'bg-transparent'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
|
||||||
|
{/* Logo */}
|
||||||
|
<motion.a
|
||||||
|
href="#"
|
||||||
|
whileHover={{ scale: 1.04 }}
|
||||||
|
className="font-display font-bold text-lg tracking-[0.22em] gold-gradient-text select-none"
|
||||||
|
>
|
||||||
|
CMUSIC
|
||||||
|
</motion.a>
|
||||||
|
|
||||||
|
{/* Desktop links */}
|
||||||
|
<div className="hidden md:flex items-center gap-8">
|
||||||
|
{navLinks.map(({ label, href }) => (
|
||||||
|
<motion.a
|
||||||
|
key={label}
|
||||||
|
href={href}
|
||||||
|
className="relative text-sm text-secondary hover:text-white transition-colors duration-200 group py-1"
|
||||||
|
>
|
||||||
|
{label}
|
||||||
|
<span className="absolute bottom-0 left-0 w-0 h-px bg-gold group-hover:w-full transition-all duration-300 ease-expo" />
|
||||||
|
</motion.a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right actions */}
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
{/* Search */}
|
||||||
|
<motion.button whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }}
|
||||||
|
className="text-secondary hover:text-white transition-colors p-1.5">
|
||||||
|
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8}
|
||||||
|
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
|
</svg>
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
|
{/* Sign in — desktop */}
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.04, borderColor: 'rgba(201,168,76,0.45)' }}
|
||||||
|
whileTap={{ scale: 0.96 }}
|
||||||
|
className="hidden md:block px-4 py-1.5 rounded-full text-xs font-medium tracking-wide border border-dark-border text-secondary hover:text-gold transition-all duration-200"
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
|
{/* Avatar */}
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.08 }}
|
||||||
|
whileTap={{ scale: 0.94 }}
|
||||||
|
className="w-8 h-8 rounded-full flex items-center justify-center text-xs font-bold text-dark-base gold-glow-sm"
|
||||||
|
style={{ background: 'linear-gradient(135deg, #c9a84c, #e8c547)' }}
|
||||||
|
>
|
||||||
|
A
|
||||||
|
</motion.button>
|
||||||
|
|
||||||
|
{/* Mobile hamburger */}
|
||||||
|
<motion.button
|
||||||
|
whileHover={{ scale: 1.1 }}
|
||||||
|
whileTap={{ scale: 0.9 }}
|
||||||
|
className="md:hidden text-secondary hover:text-white transition-colors p-1"
|
||||||
|
onClick={() => setMenuOpen(o => !o)}
|
||||||
|
>
|
||||||
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
{menuOpen
|
||||||
|
? <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M6 18L18 6M6 6l12 12" />
|
||||||
|
: <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.8} d="M4 6h16M4 12h16M4 18h16" />
|
||||||
|
}
|
||||||
|
</svg>
|
||||||
|
</motion.button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</motion.nav>
|
||||||
|
|
||||||
|
{/* Mobile menu */}
|
||||||
|
<AnimatePresence>
|
||||||
|
{menuOpen && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: -16 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
exit={{ opacity: 0, y: -16 }}
|
||||||
|
transition={{ duration: 0.25 }}
|
||||||
|
className="fixed top-16 left-0 right-0 z-40 glass-dark border-b border-white/5 px-6 py-4 md:hidden"
|
||||||
|
>
|
||||||
|
{navLinks.map(({ label, href }) => (
|
||||||
|
<a key={label} href={href}
|
||||||
|
onClick={() => setMenuOpen(false)}
|
||||||
|
className="block py-3 text-sm text-secondary hover:text-gold transition-colors border-b border-dark-border last:border-0">
|
||||||
|
{label}
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user