All Taste components
Navbar
Navbar — mega menu
A top bar whose Product link opens a wide panel of grouped pages with a featured card.
open state · 2 link groups with icons · featured card · footer strip · page dimmed behind
Get paid
InvoicesBranded invoices in under a minute.
Recurring billingRetainers that bill themselves.
Online paymentsCard and bank transfer on every invoice.
Run the business
RemindersPolite follow-ups, sent for you.
ReportsCash flow, late payers, tax totals.
Client portalEvery invoice and receipt, one link.
New here? Take the two-minute tourQuestions before you start? Talk to us
"use client";
import { useEffect, useRef, useState } from "react";
import { ArrowRight, BarChart3, ChevronDown, CreditCard, FileText, Mail, Repeat, Users, type LucideIcon } from "lucide-react";
import { cn } from "@/lib/utils";
type MenuLink = { icon: LucideIcon; title: string; text: string; href: string };
const groups: { label: string; links: MenuLink[] }[] = [
{
label: "Get paid",
links: [
{ icon: FileText, title: "Invoices", text: "Branded invoices in under a minute.", href: "/invoices" },
{ icon: Repeat, title: "Recurring billing", text: "Retainers that bill themselves.", href: "/recurring" },
{ icon: CreditCard, title: "Online payments", text: "Card and bank transfer on every invoice.", href: "/payments" },
],
},
{
label: "Run the business",
links: [
{ icon: Mail, title: "Reminders", text: "Polite follow-ups, sent for you.", href: "/reminders" },
{ icon: BarChart3, title: "Reports", text: "Cash flow, late payers, tax totals.", href: "/reports" },
{ icon: Users, title: "Client portal", text: "Every invoice and receipt, one link.", href: "/portal" },
],
},
];
const links = [
{ label: "Pricing", href: "/pricing" },
{ label: "Customers", href: "/customers" },
{ label: "Resources", href: "/resources" },
];
export function NavbarMegaMenu() {
const [open, setOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
// Close on Escape and on a click outside the header.
useEffect(() => {
if (!open) return;
const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
const onClick = (e: MouseEvent) => { if (!ref.current?.contains(e.target as Node)) setOpen(false); };
document.addEventListener("keydown", onKey);
document.addEventListener("mousedown", onClick);
return () => { document.removeEventListener("keydown", onKey); document.removeEventListener("mousedown", onClick); };
}, [open]);
return (
<div ref={ref} className="relative z-40 font-sans">
<header className="border-b border-border bg-bg">
<div className="mx-auto flex h-16 max-w-6xl items-center gap-6 px-4 sm:px-7">
<a href="/" className="flex items-center gap-2 text-base font-semibold tracking-tight text-fg">
<span aria-hidden className="size-6 rounded-sm bg-primary" />
Fernbill
</a>
<nav aria-label="Main" className="hidden md:block">
<ul className="flex items-center gap-0.5">
<li>
<button
type="button"
aria-expanded={open}
aria-controls="product-menu"
onClick={() => setOpen(!open)}
className="inline-flex h-9 items-center gap-1 rounded px-3 text-sm text-fg-muted transition hover:bg-surface hover:text-fg aria-expanded:bg-surface aria-expanded:text-fg"
>
Product <ChevronDown className={cn("size-3.5 transition-transform", open && "rotate-180")} aria-hidden />
</button>
</li>
{links.map((l) => (
<li key={l.href}>
<a href={l.href} className="inline-flex h-9 items-center rounded px-3 text-sm text-fg-muted transition hover:bg-surface hover:text-fg">{l.label}</a>
</li>
))}
</ul>
</nav>
<div className="ml-auto flex items-center gap-2">
<a href="/login" className="hidden h-9 items-center rounded px-3 text-sm font-medium text-fg hover:bg-surface-2 sm:inline-flex">Log in</a>
<a href="/signup" className="inline-flex h-9 items-center rounded bg-primary px-3.5 text-sm font-medium text-primary-fg transition hover:bg-primary-hover">Start free</a>
</div>
</div>
</header>
{open && (
<div id="product-menu" className="absolute inset-x-0 top-full border-b border-border bg-bg shadow-xl">
<div className="mx-auto grid max-w-6xl gap-4 p-4 md:grid-cols-[1fr_1fr_0.9fr] md:gap-7 md:px-7 md:py-7">
{groups.map((g) => (
<div key={g.label}>
<p className="mb-1.5 ml-2.5 text-[11px] font-medium uppercase tracking-[0.12em] text-fg-subtle">{g.label}</p>
{g.links.map(({ icon: Icon, ...l }) => (
<a key={l.href} href={l.href} className="flex gap-3 rounded-lg p-2.5 transition hover:bg-surface">
<span className="grid size-9 shrink-0 place-items-center rounded border border-border bg-bg text-fg shadow-sm">
<Icon className="size-[18px]" aria-hidden />
</span>
<span>
<span className="block text-sm font-medium text-fg">{l.title}</span>
<span className="block text-[13px] leading-snug text-fg-muted">{l.text}</span>
</span>
</a>
))}
</div>
))}
<a href="/changelog/currencies" className="flex min-h-[200px] flex-col justify-end gap-2.5 rounded-lg border border-border bg-surface bg-[radial-gradient(120%_90%_at_100%_0%,color-mix(in_srgb,var(--tst-primary)_30%,transparent),transparent_60%)] p-5">
<span className="w-fit rounded-full bg-primary-soft px-2 py-0.5 text-xs font-medium text-primary-soft-fg">New</span>
<span className="text-base font-semibold tracking-tight text-fg">Get paid in 24 currencies</span>
<span className="text-[13px] leading-relaxed text-fg-muted">Bill clients abroad in their money and receive yours, with the rate shown up front.</span>
<span className="inline-flex items-center gap-1 text-sm font-medium text-primary">Read the update <ArrowRight className="size-3.5" aria-hidden /></span>
</a>
</div>
<div className="mx-auto flex max-w-6xl flex-wrap justify-between gap-2 border-t border-border px-4 py-3.5 text-[13px] text-fg-muted md:px-9">
<span>New here? <a href="/tour" className="font-medium text-fg">Take the two-minute tour</a></span>
<span>Questions before you start? <a href="/contact" className="font-medium text-fg">Talk to us</a></span>
</div>
</div>
)}
</div>
);
}
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.