Innovations

Mega Dropdown Navbar

Top nav with a multi-column dropdown panel that opens on click; closes on outside click or ESC.

Preview

Source

tsx
"use client";

import { useEffect, useRef, useState } from "react";
import { Button } from "@/components/ui/button";
import { Sparkles, ChevronDown } from "lucide-react";

const megaColumns = [
  {
    heading: "Products",
    items: [
      { label: "Analytics", desc: "Track and measure growth", href: "#" },
      { label: "Automation", desc: "Workflows that run themselves", href: "#" },
      { label: "Insights", desc: "AI-generated recommendations", href: "#" },
    ],
  },
  {
    heading: "Solutions",
    items: [
      { label: "For startups", desc: "Move fast from day one", href: "#" },
      { label: "For enterprise", desc: "Scale with confidence", href: "#" },
      { label: "For agencies", desc: "Manage clients at scale", href: "#" },
    ],
  },
  {
    heading: "Resources",
    items: [
      { label: "Docs", desc: "Guides and API reference", href: "#" },
      { label: "Blog", desc: "Product updates and essays", href: "#" },
      { label: "Community", desc: "Forums, Discord, events", href: "#" },
    ],
  },
];

export default function NavbarMegaDropdown() {
  const [open, setOpen] = useState(false);
  const wrapperRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    const onClick = (e: MouseEvent) => {
      if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) {
        setOpen(false);
      }
    };
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") setOpen(false);
    };
    document.addEventListener("mousedown", onClick);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onClick);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);

  return (
    <header className="w-full border-b border-border bg-background relative">
      <div className="container mx-auto flex items-center justify-between gap-6 px-6 py-4">
        <a href="#" className="flex items-center gap-2 text-foreground font-bold">
          <Sparkles className="w-5 h-5 text-primary" />
          <span>Acme</span>
        </a>
        <nav className="hidden md:flex items-center gap-6" ref={wrapperRef}>
          <button
            type="button"
            onClick={() => setOpen((v) => !v)}
            aria-expanded={open}
            className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
          >
            Explore
            <ChevronDown className={`w-4 h-4 transition-transform ${open ? "rotate-180" : ""}`} />
          </button>
          <a href="#" className="text-sm text-muted-foreground hover:text-foreground transition-colors">Pricing</a>
          <a href="#" className="text-sm text-muted-foreground hover:text-foreground transition-colors">Customers</a>
          <a href="#" className="text-sm text-muted-foreground hover:text-foreground transition-colors">Company</a>
        </nav>
        <Button size="sm">Get started</Button>
      </div>

      {open && (
        <div className="absolute left-0 right-0 top-full border-t border-border bg-background shadow-xl z-40">
          <div className="container mx-auto grid grid-cols-1 md:grid-cols-3 gap-8 px-6 py-8">
            {megaColumns.map((col) => (
              <div key={col.heading}>
                <h3 className="text-xs font-semibold uppercase tracking-widest text-muted-foreground mb-4">
                  {col.heading}
                </h3>
                <ul className="space-y-3">
                  {col.items.map((it) => (
                    <li key={it.label}>
                      <a
                        href={it.href}
                        className="block rounded-md p-2 -m-2 hover:bg-accent transition-colors"
                      >
                        <div className="text-sm font-medium text-foreground">{it.label}</div>
                        <div className="text-xs text-muted-foreground mt-0.5">{it.desc}</div>
                      </a>
                    </li>
                  ))}
                </ul>
              </div>
            ))}
          </div>
        </div>
      )}
    </header>
  );
}
Claude Code Instructions

CLI Install

npx innovations add mega-dropdown

Where to use it

Use when your product has enough surface area to warrant grouping (multiple products, solutions, resources). In Astro (src/layouts/Layout.astro): import NavbarMegaDropdown from '../components/innovations/navbars/mega-dropdown'; Customize: edit the megaColumns array at the top. Each column has a heading and items with label + desc + href. You can add more columns — the grid will adapt (consider md:grid-cols-4 if you add a 4th).