← NavbarOpen in v0
Navbar 02 — Classic Top Bar
Full-width sticky top bar with blur, logo left, centered links, ghost and primary CTAs and a mobile dropdown menu.
$ npx shadcn@latest add @ludusvibe/navbar-02Code
Installed to components/sections/navbar-02.tsx — it's yours to edit.
"use client"
import * as React from "react"
import { motion } from "motion/react"
import { Menu, X } from "lucide-react"
import { buttonVariants } from "@/components/ui/button"
import { fadeUp } from "@/lib/motion"
import { cn } from "@/lib/utils"
type NavLink = { label: string; href: string }
type Cta = { label: string; href: string }
export type Navbar02Props = {
/** Text logo — pass a string or any node. */
logo?: React.ReactNode
logoHref?: string
links?: NavLink[]
/** Ghost-styled link, e.g. sign in. */
secondaryCta?: Cta
/** Primary button-styled link. */
primaryCta?: Cta
className?: string
}
export function Navbar02({
logo = "Lumen",
logoHref = "#",
links = [
{ label: "Product", href: "#product" },
{ label: "Customers", href: "#customers" },
{ label: "Pricing", href: "#pricing" },
{ label: "Docs", href: "#docs" },
{ label: "Blog", href: "#blog" },
],
secondaryCta = { label: "Sign in", href: "#" },
primaryCta = { label: "Start free trial", href: "#" },
className,
}: Navbar02Props) {
const [open, setOpen] = React.useState(false)
const panelId = React.useId()
return (
<header
className={cn(
"sticky top-0 z-50 w-full border-b bg-background/80 backdrop-blur-md",
className
)}
>
<nav
aria-label="Main"
className="relative mx-auto flex h-16 max-w-6xl items-center justify-between px-6"
>
<a
href={logoHref}
className="flex items-center gap-2.5 font-heading text-lg font-semibold tracking-tight"
>
<span
aria-hidden
className="grid size-6 place-items-center rounded-md bg-primary/10 ring-1 ring-primary/25"
>
<span className="size-2.5 rounded-sm bg-primary" />
</span>
{logo}
</a>
<div className="absolute top-1/2 left-1/2 hidden -translate-x-1/2 -translate-y-1/2 items-center gap-1 md:flex">
{links.map((link) => (
<a
key={link.label}
href={link.href}
className="rounded-lg px-3 py-1.5 text-sm text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
>
{link.label}
</a>
))}
</div>
<div className="flex items-center gap-1.5">
<a
href={secondaryCta.href}
className={cn(
buttonVariants({ variant: "ghost" }),
"hidden text-muted-foreground sm:inline-flex"
)}
>
{secondaryCta.label}
</a>
<a href={primaryCta.href} className={buttonVariants()}>
{primaryCta.label}
</a>
<button
type="button"
aria-expanded={open}
aria-controls={panelId}
onClick={() => setOpen((v) => !v)}
className={cn(
buttonVariants({ variant: "ghost", size: "icon-sm" }),
"md:hidden"
)}
>
{open ? <X /> : <Menu />}
<span className="sr-only">{open ? "Close menu" : "Open menu"}</span>
</button>
</div>
</nav>
{open && (
<motion.div
id={panelId}
initial="hidden"
animate="visible"
variants={fadeUp}
className="border-t bg-background/95 backdrop-blur-md md:hidden"
>
<div className="mx-auto flex max-w-6xl flex-col gap-1 px-6 py-4">
{links.map((link) => (
<a
key={link.label}
href={link.href}
onClick={() => setOpen(false)}
className="rounded-lg px-3 py-2.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
>
{link.label}
</a>
))}
<a
href={secondaryCta.href}
onClick={() => setOpen(false)}
className="rounded-lg px-3 py-2.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground sm:hidden"
>
{secondaryCta.label}
</a>
</div>
</motion.div>
)}
</header>
)
}