Component
Curved Navbar
The Curved Navigation Menu is an elegantly designed user interface component built with React.js, TailwindCSS, and Framer Motion. This menu provides a smooth and interactive way to navigate through different sections of a website, enhancing the user experience with modern animations and a sleek curved design
Features
- ▸Responsive Design : Tailored to work seamlessly across various devices and screen sizes, ensuring an optimal user experience on mobile, tablet, and desktop.
- ▸Smooth Animations : Utilizes Framer Motion to deliver fluid and engaging animations, enhancing the visual appeal and interaction quality.
- ▸Interactive Hover Effects : Dynamic hover effects that highlight menu items and provide visual feedback to users, improving navigation clarity.
- ▸Customizable Styles : Built with TailwindCSS, offering extensive customization options to fit different design needs and themes.
- ▸User-Friendly : Intuitive navigation structure that makes it easy for users to explore the website's content.
LOGO
Installation
01.Install Dependencies
npm install motion react-iconsSource Code
'use client'
import { useState } from 'react';
import { usePathname } from 'next/navigation';
import { motion } from 'motion/react';
import { IoClose } from "react-icons/io5";
import Link from 'next/link';
const navItems = [
{
title: "Home",
href: "/",
},
{
title: "Work",
href: "/work",
},
{
title: "About",
href: "/about",
},
{
title: "Contact",
href: "/contact",
},
]
const menuSlide = {
initial: {x: "calc(100% + 100px)"},
enter: {x: "0", transition: {duration: 0.8, ease: [0.76, 0, 0.24, 1]}},
exit: {x: "calc(100% + 100px)", transition: {duration: 0.8, ease: [0.76, 0, 0.24, 1]}}
}
const slide = {
initial: {x: 80},
enter: i => ({x: 0, transition: {duration: 0.8, ease: [0.76, 0, 0.24, 1], delay: 0.05 * i}}),
exit: i => ({x: 80, transition: {duration: 0.8, ease: [0.76, 0, 0.24, 1], delay: 0.05 * i}})
}
const scale = {
open: {scale: 1, transition: {duration: 0.3}},
closed: {scale: 0, transition: {duration: 0.4}}
}
function Curve() {
const initialPath = `M100 0 L200 0 L200 ${window.innerHeight} L100 ${window.innerHeight} Q-100 ${window.innerHeight/2} 100 0`
const targetPath = `M100 0 L200 0 L200 ${window.innerHeight} L100 ${window.innerHeight} Q100 ${window.innerHeight/2} 100 0`
const curve = {
initial: {
d: initialPath
},
enter: {
d: targetPath,
transition: {duration: 1, ease: [0.76, 0, 0.24, 1]}
},
exit: {
d: initialPath,
transition: {duration: 0.8, ease: [0.76, 0, 0.24, 1]}
}
}
return (
<svg className={`absolute top-0 -left-[99px] w-[100px] stroke-none h-full fill-[rgb(255,224,69)]`}>
<motion.path variants={curve} initial="initial" animate="enter" exit="exit"></motion.path>
</svg>
)
}
function Footer() {
return (
<div className={`flex w-full text-sm justify-between`}>
<a>Link 1</a>
<a>Link 2</a>
<a>Link 3</a>
<a>Link 4</a>
</div>
)
}
function NavLink({data, isActive, setSelectedIndicator}) {
const { title, href, index} = data;
return (
<motion.div
className={`relative flex items-center`}
onMouseEnter={() => {setSelectedIndicator(href)}}
custom={index}
variants={slide}
initial="initial"
animate="enter"
exit="exit"
>
<motion.div
variants={scale}
animate={isActive ? "open" : "closed"}
className={`w-2.5 h-2.5 bg-black rounded-full absolute -left-8`}
></motion.div>
<Link href={href} className='uppercase font-black'>{title}</Link>
</motion.div>
)
}
export default function CurvedNavbar({isActive, setIsActive}) {
const pathname = usePathname();
const [selectedIndicator, setSelectedIndicator] = useState(pathname);
return (
<motion.div
variants={menuSlide}
initial="initial"
animate="enter"
exit="exit"
className={`h-screen w-screen max-w-screen-sm fixed right-0 top-0 text-black bg-[rgb(255,224,69)] z-10`}
>
<div className='w-full flex justify-end text-3xl p-4' onClick={() => setIsActive(false)}>
<IoClose className='text-3xl'/>
</div>
<div className={`h-full p-24 flex flex-col justify-between`}>
<div onMouseLeave={() => {setSelectedIndicator(pathname)}} className={`flex flex-col text-5xl gap-3 mt-20`}>
<div className={`text-gray-900 border-b border-gray-800 uppercase text-sm mb-10`}>
<p>Brand Logo</p>
</div>
{
navItems.map((data, index) => {
return <NavLink key={index} data={{...data, index}} isActive={selectedIndicator == data.href} setSelectedIndicator={setSelectedIndicator}></NavLink>
})
}
</div>
<Footer />
</div>
<Curve />
</motion.div>
)
}