Header 2: mega menu with mobile sheet
Sticky header with brand, a mega menu of grouped links and a featured card, sign-in and primary actions. On phones the same links open in a slide-in sheet with accordions.
// Ballmac UI: Header 2. https://ui.ballmac.com/blocks/header-2
"use client"
import * as React from "react"
import { BarChart3, Blocks, BookOpen, Code2, Layers, LifeBuoy, Menu, ShieldCheck, Sparkles, Users, Workflow } from "lucide-react"
import { buttonVariants } from "@/components/ballmac/button"
import { MegaMenu, MegaMenuMobileList, type MegaMenuItem } from "@/components/ballmac/mega-menu"
import { Sheet, SheetBody, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ballmac/sheet"
import { cn } from "@/lib/utils"
type Action = { label: string; href: string }
type Header2Props = React.ComponentProps<"header"> & {
/** Brand name beside the logo mark. */
brand?: string
/** Where the brand links to. */
brandHref?: string
/** Logo mark. Defaults to a simple rounded square with the brand's first letter. */
logo?: React.ReactNode
/** Navigation: links, or panels with grouped links and an optional featured card. */
items?: MegaMenuItem[]
/** Quiet action on the right, e.g. sign in. */
secondaryAction?: Action
/** Main action on the right. */
primaryAction?: Action
/** Stick to the top of the page with a blurred background. */
sticky?: boolean
}
const defaultItems: MegaMenuItem[] = [
{
label: "Product",
columns: [
{
title: "Build",
links: [
{ title: "Workspaces", href: "#", description: "Projects, files and approvals in one place.", icon: <Layers /> },
{ title: "Automations", href: "#", description: "Run tasks when things change.", icon: <Workflow />, badge: "New" },
{ title: "API", href: "#", description: "Build on the same data.", icon: <Code2 /> },
],
},
{
title: "Run",
links: [
{ title: "Analytics", href: "#", description: "Usage and performance at a glance.", icon: <BarChart3 /> },
{ title: "Security", href: "#", description: "Roles, audit logs and sign-in policies.", icon: <ShieldCheck /> },
{ title: "Integrations", href: "#", description: "Connect the tools you already use.", icon: <Blocks /> },
],
},
],
featured: { title: "What’s new", description: "See the latest releases and what they change for your team.", href: "#", media: <Sparkles />, cta: "Read the changelog" },
},
{
label: "Solutions",
columns: [
{
links: [
{ title: "Startups", href: "#", description: "Move fast with a small team.", icon: <Users /> },
{ title: "Enterprise", href: "#", description: "Controls for larger organizations.", icon: <ShieldCheck /> },
],
},
],
},
{
label: "Resources",
columns: [
{
links: [
{ title: "Documentation", href: "#", description: "Guides and API reference.", icon: <BookOpen /> },
{ title: "Help center", href: "#", description: "Answers from our support team.", icon: <LifeBuoy /> },
],
},
],
},
{ label: "Pricing", href: "#" },
]
function Header2({
brand = "Acme",
brandHref = "#",
logo,
items = defaultItems,
secondaryAction = { label: "Sign in", href: "#" },
primaryAction = { label: "Get started", href: "#" },
sticky = true,
className,
...props
}: Header2Props) {
const [open, setOpen] = React.useState(false)
return (
<header data-slot="header-2" className={cn("bg-background/80 z-40 w-full border-b backdrop-blur-xl", sticky && "sticky top-0", className)} {...props}>
<div className="mx-auto flex h-16 max-w-6xl items-center gap-6 px-4 sm:px-6">
<a href={brandHref} className="focus-visible:ring-ring/50 flex items-center gap-2.5 rounded-md text-lg font-semibold tracking-tight outline-none focus-visible:ring-[3px]">
{logo ?? (
<span aria-hidden="true" className="bg-foreground text-background flex size-7 items-center justify-center rounded-lg text-sm font-bold">
{brand[0]}
</span>
)}
{brand}
</a>
<MegaMenu items={items} label="Main" viewportAlign="start" />
<div className="ms-auto hidden items-center gap-2 md:flex">
<a className={buttonVariants({ variant: "ghost" })} href={secondaryAction.href}>{secondaryAction.label}</a>
<a className={buttonVariants({ shape: "pill" })} href={primaryAction.href}>{primaryAction.label}</a>
</div>
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger className={buttonVariants({ variant: "ghost", size: "icon", className: "ms-auto md:hidden" })} aria-label="Open menu">
<Menu />
</SheetTrigger>
<SheetContent side="end" closeLabel="Close menu" className="w-[min(22rem,90vw)]">
<SheetHeader>
<SheetTitle>{brand}</SheetTitle>
<SheetDescription className="sr-only">Site navigation</SheetDescription>
</SheetHeader>
<SheetBody>
<MegaMenuMobileList items={items} onNavigate={() => setOpen(false)} />
</SheetBody>
<SheetFooter className="grid gap-2">
<a className={buttonVariants({ shape: "pill", size: "lg" })} href={primaryAction.href}>{primaryAction.label}</a>
<a className={buttonVariants({ variant: "outline", shape: "pill", size: "lg" })} href={secondaryAction.href}>{secondaryAction.label}</a>
</SheetFooter>
</SheetContent>
</Sheet>
</div>
</header>
)
}
export { Header2, type Header2Props }Installation
$ pnpm dlx shadcn@latest add @ballmac/header-2Install the dependencies.
$ pnpm add lucide-reactAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/button @ballmac/mega-menu @ballmac/sheetCopy the source into your project.
components/ballmac/blocks/header-2/header-2.tsx// Ballmac UI: Header 2. https://ui.ballmac.com/blocks/header-2 "use client" import * as React from "react" import { BarChart3, Blocks, BookOpen, Code2, Layers, LifeBuoy, Menu, ShieldCheck, Sparkles, Users, Workflow } from "lucide-react" import { buttonVariants } from "@/components/ballmac/button" import { MegaMenu, MegaMenuMobileList, type MegaMenuItem } from "@/components/ballmac/mega-menu" import { Sheet, SheetBody, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ballmac/sheet" import { cn } from "@/lib/utils" type Action = { label: string; href: string } type Header2Props = React.ComponentProps<"header"> & { /** Brand name beside the logo mark. */ brand?: string /** Where the brand links to. */ brandHref?: string /** Logo mark. Defaults to a simple rounded square with the brand's first letter. */ logo?: React.ReactNode /** Navigation: links, or panels with grouped links and an optional featured card. */ items?: MegaMenuItem[] /** Quiet action on the right, e.g. sign in. */ secondaryAction?: Action /** Main action on the right. */ primaryAction?: Action /** Stick to the top of the page with a blurred background. */ sticky?: boolean } const defaultItems: MegaMenuItem[] = [ { label: "Product", columns: [ { title: "Build", links: [ { title: "Workspaces", href: "#", description: "Projects, files and approvals in one place.", icon: <Layers /> }, { title: "Automations", href: "#", description: "Run tasks when things change.", icon: <Workflow />, badge: "New" }, { title: "API", href: "#", description: "Build on the same data.", icon: <Code2 /> }, ], }, { title: "Run", links: [ { title: "Analytics", href: "#", description: "Usage and performance at a glance.", icon: <BarChart3 /> }, { title: "Security", href: "#", description: "Roles, audit logs and sign-in policies.", icon: <ShieldCheck /> }, { title: "Integrations", href: "#", description: "Connect the tools you already use.", icon: <Blocks /> }, ], }, ], featured: { title: "What’s new", description: "See the latest releases and what they change for your team.", href: "#", media: <Sparkles />, cta: "Read the changelog" }, }, { label: "Solutions", columns: [ { links: [ { title: "Startups", href: "#", description: "Move fast with a small team.", icon: <Users /> }, { title: "Enterprise", href: "#", description: "Controls for larger organizations.", icon: <ShieldCheck /> }, ], }, ], }, { label: "Resources", columns: [ { links: [ { title: "Documentation", href: "#", description: "Guides and API reference.", icon: <BookOpen /> }, { title: "Help center", href: "#", description: "Answers from our support team.", icon: <LifeBuoy /> }, ], }, ], }, { label: "Pricing", href: "#" }, ] function Header2({ brand = "Acme", brandHref = "#", logo, items = defaultItems, secondaryAction = { label: "Sign in", href: "#" }, primaryAction = { label: "Get started", href: "#" }, sticky = true, className, ...props }: Header2Props) { const [open, setOpen] = React.useState(false) return ( <header data-slot="header-2" className={cn("bg-background/80 z-40 w-full border-b backdrop-blur-xl", sticky && "sticky top-0", className)} {...props}> <div className="mx-auto flex h-16 max-w-6xl items-center gap-6 px-4 sm:px-6"> <a href={brandHref} className="focus-visible:ring-ring/50 flex items-center gap-2.5 rounded-md text-lg font-semibold tracking-tight outline-none focus-visible:ring-[3px]"> {logo ?? ( <span aria-hidden="true" className="bg-foreground text-background flex size-7 items-center justify-center rounded-lg text-sm font-bold"> {brand[0]} </span> )} {brand} </a> <MegaMenu items={items} label="Main" viewportAlign="start" /> <div className="ms-auto hidden items-center gap-2 md:flex"> <a className={buttonVariants({ variant: "ghost" })} href={secondaryAction.href}>{secondaryAction.label}</a> <a className={buttonVariants({ shape: "pill" })} href={primaryAction.href}>{primaryAction.label}</a> </div> <Sheet open={open} onOpenChange={setOpen}> <SheetTrigger className={buttonVariants({ variant: "ghost", size: "icon", className: "ms-auto md:hidden" })} aria-label="Open menu"> <Menu /> </SheetTrigger> <SheetContent side="end" closeLabel="Close menu" className="w-[min(22rem,90vw)]"> <SheetHeader> <SheetTitle>{brand}</SheetTitle> <SheetDescription className="sr-only">Site navigation</SheetDescription> </SheetHeader> <SheetBody> <MegaMenuMobileList items={items} onNavigate={() => setOpen(false)} /> </SheetBody> <SheetFooter className="grid gap-2"> <a className={buttonVariants({ shape: "pill", size: "lg" })} href={primaryAction.href}>{primaryAction.label}</a> <a className={buttonVariants({ variant: "outline", shape: "pill", size: "lg" })} href={secondaryAction.href}>{secondaryAction.label}</a> </SheetFooter> </SheetContent> </Sheet> </div> </header> ) } export { Header2, type Header2Props }Update the import paths to match your project setup.
Usage
import { Header2 } from "@/components/ballmac/blocks/header-2/header-2"
/** The page below the header gives the open mega menu something to float over. */
export default function Header2Demo() {
return (
<div className="bg-background min-h-[34rem]">
<Header2 sticky={false} />
<div className="mx-auto max-w-6xl px-4 pt-20 sm:px-6">
<div className="bg-muted h-3 w-40 rounded-full" />
<div className="bg-muted mt-6 h-10 w-full max-w-xl rounded-xl" />
<div className="bg-muted mt-3 h-10 w-full max-w-md rounded-xl" />
<div className="mt-8 grid gap-4 sm:grid-cols-3">
{[0, 1, 2].map((i) => (
<div key={i} className="bg-muted/60 h-32 rounded-2xl" />
))}
</div>
</div>
</div>
)
}Dependencies
- npm
- lucide-react
Use with AI
A navigation header for products with many pages. Pass items (the same shape as mega-menu), brand, logo, secondaryAction and primaryAction. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Header 2: mega menu with mobile sheet (@ballmac/header-2) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Sites with several product areas
- Anything that outgrew four top-level links
Not for
- Simple sites with a few links (use header-1)
Registry JSON: https://ui.ballmac.com/r/header-2.json
Free to use in personal and commercial projects.
More header blocks
Header 1: sticky with mobile menu
Sticky site header with brand, main links, sign-in and primary action, and an accessible mobile menu that closes on Escape or link tap.
Header Pro 1: mega menu navigation
A top bar with menus that open full-width panels of grouped links and a feature card, with hover, click and arrow-key support, and a disclosure menu on phones.
Header Pro 2: floating pill with announcement
A dismissible announcement bar above a floating pill header that shrinks as you scroll, with a moving highlight behind the hovered link and a phone menu.