
3D Extruded Buildings & Urban Footprints
Real-world 3D building extrusions with dynamic sunlight shadows, terrain DEM elevations, and pitch/bearing camera controls.
Site header collection: sticky top bar, dismissible announcement stack, centered-logo split, floating pill, full-width mega menu, inline search with command palette, and a transparent-over-hero scroll variant — each with a mobile sheet fallback.
Also available for Vue ->$pnpm dlx shadcn@latest add https://uipkge.dev/r/react/header.json$npx shadcn@latest add https://uipkge.dev/r/react/header.json$yarn dlx shadcn@latest add https://uipkge.dev/r/react/header.json$bunx shadcn@latest add https://uipkge.dev/r/react/header.jsonnpx shadcn@latest add @uipkge-react/headerInstalls to:components/blocks/header/'use client'
import * as React from 'react'
import { Boxes, Menu, X } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
export function Header01() {
const [mobileOpen, setMobileOpen] = React.useState(false)
return (
<header
data-slot="header-01"
className="bg-background/80 supports-[backdrop-filter]:bg-background/60 sticky top-0 z-40 border-b backdrop-blur"
>
<div className="mx-auto flex h-16 max-w-6xl items-center justify-between px-6">
<a href="#" className="flex items-center gap-2">
<div className="bg-primary text-primary-foreground flex size-8 items-center justify-center rounded-md">
<Boxes className="size-4" />
</div>
<span className="text-base font-semibold">Acme</span>
</a>
<nav className="hidden items-center gap-6 md:flex" aria-label="Primary">
<a href="#features" className="text-muted-foreground hover:text-foreground text-sm transition-colors">
Features
</a>
<a href="#pricing" className="text-muted-foreground hover:text-foreground text-sm transition-colors">
Pricing
</a>
<a href="#customers" className="text-muted-foreground hover:text-foreground text-sm transition-colors">
Customers
</a>
<a href="#docs" className="text-muted-foreground hover:text-foreground text-sm transition-colors">
Docs
</a>
<a href="#blog" className="text-muted-foreground hover:text-foreground text-sm transition-colors">
Blog
</a>
</nav>
<div className="hidden items-center gap-2 md:flex">
<Button variant="ghost" size="sm">
Sign in
</Button>
<Button size="sm">Start free trial</Button>
</div>
<Sheet open={mobileOpen} onOpenChange={setMobileOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="md:hidden" aria-label="Open menu">
<Menu className="size-5" />
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-72">
<div className="flex h-full flex-col">
<div className="flex items-center justify-between border-b px-4 py-3">
<div className="flex items-center gap-2">
<div className="bg-primary text-primary-foreground flex size-7 items-center justify-center rounded-md">
<Boxes className="size-3.5" />
</div>
<span className="text-sm font-semibold">Acme</span>
</div>
<Button
variant="ghost"
size="icon"
className="size-8"
aria-label="Close menu"
onClick={() => setMobileOpen(false)}
>
<X className="size-4" />
</Button>
</div>
<nav className="flex flex-1 flex-col gap-1 p-4" aria-label="Mobile">
<a
href="#features"
className="hover:bg-muted rounded-md px-3 py-2 text-sm transition-colors"
onClick={() => setMobileOpen(false)}
>
Features
</a>
<a
href="#pricing"
className="hover:bg-muted rounded-md px-3 py-2 text-sm transition-colors"
onClick={() => setMobileOpen(false)}
>
Pricing
</a>
<a
href="#customers"
className="hover:bg-muted rounded-md px-3 py-2 text-sm transition-colors"
onClick={() => setMobileOpen(false)}
>
Customers
</a>
<a
href="#docs"
className="hover:bg-muted rounded-md px-3 py-2 text-sm transition-colors"
onClick={() => setMobileOpen(false)}
>
Docs
</a>
<a
href="#blog"
className="hover:bg-muted rounded-md px-3 py-2 text-sm transition-colors"
onClick={() => setMobileOpen(false)}
>
Blog
</a>
</nav>
<div className="flex flex-col gap-2 border-t p-4">
<Button variant="outline" className="w-full">
Sign in
</Button>
<Button className="w-full">Start free trial</Button>
</div>
</div>
</SheetContent>
</Sheet>
</div>
</header>
)
}
'use client'
import { useEffect, useState } from 'react'
import { ArrowRight, Menu, Layers, X } from 'lucide-react'
import { Badge } from '@/components/ui/badge'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
const STORAGE_KEY = 'uipkge:announcement-dismissed'
const links = [
{ label: 'Platform', href: '#platform' },
{ label: 'Pricing', href: '#pricing' },
{ label: 'Customers', href: '#customers' },
{ label: 'Docs', href: '#docs' },
]
export function HeaderAnnouncementStack() {
// Rendered on the server and dismissed only on the client, so there is no
// flash of a strip that then vanishes.
const [dismissed, setDismissed] = useState(false)
const [open, setOpen] = useState(false)
useEffect(() => {
try {
setDismissed(window.sessionStorage.getItem(STORAGE_KEY) === '1')
} catch {
setDismissed(false)
}
}, [])
function dismiss() {
setDismissed(true)
try {
window.sessionStorage.setItem(STORAGE_KEY, '1')
} catch {
// Blocked storage: the dismissal still holds for this page view.
}
}
return (
<div data-slot="header-announcement-stack" className="sticky top-0 z-40">
{!dismissed && (
<div className="border-border bg-muted/60 border-b backdrop-blur">
<div className="mx-auto flex h-10 max-w-6xl items-center gap-3 px-6 text-sm">
<Badge variant="secondary" className="shrink-0">
New
</Badge>
<p className="min-w-0 truncate">
Row-level scoping now evaluates against SCIM groups.{' '}
<a href="#changelog" className="hover:text-foreground underline underline-offset-4">
Read the changelog
</a>
</p>
<ArrowRight className="text-muted-foreground hidden size-3.5 shrink-0 sm:block" aria-hidden="true" />
<Button
variant="ghost"
size="icon"
className="ml-auto size-7 shrink-0"
aria-label="Dismiss announcement"
onClick={dismiss}
>
<X className="size-3.5" aria-hidden="true" />
</Button>
</div>
</div>
)}
<header className="border-border bg-background/90 border-b backdrop-blur">
<div className="mx-auto flex h-16 max-w-6xl items-center gap-6 px-6">
<a href="#top" className="flex shrink-0 items-center gap-2 font-semibold tracking-tight">
<Layers className="text-primary size-5" aria-hidden="true" />
Northwind
</a>
<nav className="hidden items-center gap-1 md:flex" aria-label="Primary">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className="text-muted-foreground hover:text-foreground hover:bg-muted focus-visible:ring-ring rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none"
>
{link.label}
</a>
))}
</nav>
<div className="ml-auto hidden items-center gap-2 md:flex">
<Button variant="ghost" size="sm">
Sign in
</Button>
<Button size="sm">Book a demo</Button>
</div>
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="ml-auto md:hidden" aria-label="Open navigation menu">
<Menu className="size-5" aria-hidden="true" />
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-72">
<SheetHeader>
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<nav className="mt-6 grid gap-1" aria-label="Mobile">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className="hover:bg-muted rounded-md px-3 py-2 text-sm font-medium transition-colors"
onClick={() => setOpen(false)}
>
{link.label}
</a>
))}
</nav>
<Separator className="my-5" />
<div className="grid gap-2">
<Button variant="outline">Sign in</Button>
<Button>Book a demo</Button>
</div>
</SheetContent>
</Sheet>
</div>
</header>
</div>
)
}
'use client'
import { useState } from 'react'
import { Compass, Menu, ShoppingBag, User } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
const leftLinks = [
{ label: 'Collections', href: '#collections' },
{ label: 'New in', href: '#new' },
{ label: 'Studio', href: '#studio' },
]
const rightLinks = [
{ label: 'Journal', href: '#journal' },
{ label: 'Stockists', href: '#stockists' },
{ label: 'Contact', href: '#contact' },
]
const linkClass =
'text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none'
export function HeaderCenteredLogo() {
const [open, setOpen] = useState(false)
return (
<header data-slot="header-centered-logo" className="border-border bg-background border-b">
<div className="mx-auto flex h-16 max-w-6xl items-center px-6">
{/* Equal-basis flanks keep the wordmark optically centred regardless of
how long the link labels are. */}
<nav className="hidden flex-1 basis-0 items-center gap-1 lg:flex" aria-label="Primary left">
{leftLinks.map((link) => (
<a key={link.href} href={link.href} className={linkClass}>
{link.label}
</a>
))}
</nav>
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="lg:hidden" aria-label="Open navigation menu">
<Menu className="size-5" aria-hidden="true" />
</Button>
</SheetTrigger>
<SheetContent side="left" className="w-72">
<SheetHeader>
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<nav className="mt-6 grid gap-1" aria-label="Mobile">
{[...leftLinks, ...rightLinks].map((link) => (
<a
key={link.href}
href={link.href}
className="hover:bg-muted rounded-md px-3 py-2 text-sm font-medium transition-colors"
onClick={() => setOpen(false)}
>
{link.label}
</a>
))}
</nav>
<Separator className="my-5" />
<Button className="w-full">Account</Button>
</SheetContent>
</Sheet>
<a
href="#top"
className="mx-auto flex items-center gap-2 text-base font-semibold tracking-[0.18em] uppercase lg:mx-6"
>
<Compass className="text-primary size-4" aria-hidden="true" />
Halden
</a>
<nav className="hidden flex-1 basis-0 items-center justify-end gap-1 lg:flex" aria-label="Primary right">
{rightLinks.map((link) => (
<a key={link.href} href={link.href} className={linkClass}>
{link.label}
</a>
))}
</nav>
<div className="flex items-center gap-1 lg:ml-4">
<Button variant="ghost" size="icon" aria-label="Account">
<User className="size-4" aria-hidden="true" />
</Button>
<Button variant="ghost" size="icon" aria-label="Bag">
<ShoppingBag className="size-4" aria-hidden="true" />
</Button>
</div>
</div>
</header>
)
}
'use client'
import { useCallback, useEffect, useRef, useState } from 'react'
import { CircleDot, Menu } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
const links = [
{ label: 'Product', href: '#product' },
{ label: 'Pricing', href: '#pricing' },
{ label: 'Customers', href: '#customers' },
{ label: 'Docs', href: '#docs' },
]
export function HeaderFloatingPill() {
const [active, setActive] = useState(0)
const [open, setOpen] = useState(false)
const navRef = useRef<HTMLElement | null>(null)
// Measured from the live element, so the indicator fits any label length or
// font — no hard-coded widths to drift out of sync with the copy.
const [indicator, setIndicator] = useState({ left: 0, width: 0 })
const measure = useCallback(
(index = active) => {
const el = navRef.current?.querySelectorAll<HTMLElement>('[data-pill-link]')[index]
if (el) setIndicator({ left: el.offsetLeft, width: el.offsetWidth })
},
[active],
)
useEffect(() => {
measure()
const nav = navRef.current
if (!nav || typeof ResizeObserver === 'undefined') return
const observer = new ResizeObserver(() => measure())
observer.observe(nav)
return () => observer.disconnect()
}, [measure])
return (
<div data-slot="header-floating-pill" className="pointer-events-none sticky top-0 z-40 px-4 pt-4">
<header className="border-border bg-card/85 pointer-events-auto mx-auto flex h-14 max-w-4xl items-center gap-3 rounded-full border px-3 shadow-sm backdrop-blur">
<a href="#top" className="flex shrink-0 items-center gap-2 pl-2 font-semibold tracking-tight">
<CircleDot className="text-primary size-4" aria-hidden="true" />
Verity
</a>
<nav ref={navRef} className="relative hidden items-center md:flex" aria-label="Primary">
<span
className="bg-muted absolute inset-y-1 rounded-full transition-colors duration-200 ease-out"
style={{ left: `${indicator.left}px`, width: `${indicator.width}px` }}
aria-hidden="true"
/>
{links.map((link, index) => (
<a
key={link.href}
href={link.href}
data-pill-link
className={`focus-visible:ring-ring relative rounded-full px-3.5 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none ${
index === active ? 'text-foreground' : 'text-muted-foreground hover:text-foreground'
}`}
aria-current={index === active ? 'page' : undefined}
onMouseEnter={() => measure(index)}
onMouseLeave={() => measure()}
onFocus={() => measure(index)}
onBlur={() => measure()}
onClick={() => setActive(index)}
>
{link.label}
</a>
))}
</nav>
<Button size="sm" className="ml-auto rounded-full">
Start free
</Button>
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="rounded-full md:hidden" aria-label="Open navigation menu">
<Menu className="size-5" aria-hidden="true" />
</Button>
</SheetTrigger>
<SheetContent side="top">
<SheetHeader>
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<nav className="mt-6 grid gap-1" aria-label="Mobile">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className="hover:bg-muted rounded-md px-3 py-2 text-sm font-medium transition-colors"
onClick={() => setOpen(false)}
>
{link.label}
</a>
))}
</nav>
</SheetContent>
</Sheet>
</header>
</div>
)
}
'use client'
import { useState } from 'react'
import { ArrowRight, BarChart3, BookOpen, Boxes, LifeBuoy, Lock, Menu, Layers, Workflow } from 'lucide-react'
import { Button } from '@/components/ui/button'
import {
NavigationMenu,
NavigationMenuContent,
NavigationMenuItem,
NavigationMenuList,
NavigationMenuTrigger,
navigationMenuTriggerStyle,
} from '@/components/ui/navigation-menu'
import { Separator } from '@/components/ui/separator'
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
const menus = [
{
label: 'Product',
groups: [
{
heading: 'Core',
items: [
{ icon: Boxes, title: 'Metric layer', body: 'Versioned definitions your whole org shares.' },
{ icon: BarChart3, title: 'Dashboards', body: 'Composed from certified metrics, not raw SQL.' },
{ icon: Workflow, title: 'Pipelines', body: 'Scheduled materialisation with cost ceilings.' },
],
},
{
heading: 'Governance',
items: [
{ icon: Lock, title: 'Row-level access', body: 'Scope evaluated per query, per identity.' },
{ icon: BookOpen, title: 'Change history', body: 'Every definition edit reviewed and revertible.' },
{ icon: LifeBuoy, title: 'Audit exports', body: 'Generated from the same history, not by hand.' },
],
},
],
highlight: {
title: 'Ship your first dashboard today',
body: 'Connect a warehouse, publish one certified metric, and invite the team. No migration required.',
cta: 'Start the walkthrough',
},
},
{
label: 'Resources',
groups: [
{
heading: 'Learn',
items: [
{ icon: BookOpen, title: 'Documentation', body: 'Setup, modelling, and the access model.' },
{ icon: Layers, title: 'Guides', body: 'Reconciliation and first-close checklists.' },
{ icon: BarChart3, title: 'Benchmarks', body: 'Close speed across 240 finance teams.' },
],
},
{
heading: 'Support',
items: [
{ icon: LifeBuoy, title: 'Help centre', body: 'Answers to the questions support gets most.' },
{ icon: Workflow, title: 'Status', body: 'Live uptime and incident history.' },
{ icon: Lock, title: 'Trust centre', body: 'SOC 2, subprocessors, and data residency.' },
],
},
],
highlight: {
title: 'Read the rollout plan',
body: 'The five-week implementation we run for every customer, written down so you can run it yourself.',
cta: 'Open the plan',
},
},
]
const plainLinks = [
{ label: 'Pricing', href: '#pricing' },
{ label: 'Customers', href: '#customers' },
]
export function HeaderMegaMenu() {
const [open, setOpen] = useState(false)
return (
<header data-slot="header-mega-menu" className="border-border bg-background/90 border-b backdrop-blur">
<div className="mx-auto flex h-16 max-w-6xl items-center gap-6 px-6">
<a href="#top" className="flex shrink-0 items-center gap-2 font-semibold tracking-tight">
<Layers className="text-primary size-5" aria-hidden="true" />
Northwind
</a>
<NavigationMenu className="hidden lg:flex">
<NavigationMenuList>
{menus.map((menu) => (
<NavigationMenuItem key={menu.label}>
<NavigationMenuTrigger>{menu.label}</NavigationMenuTrigger>
<NavigationMenuContent>
<div className="grid w-[46rem] grid-cols-[1fr_1fr_16rem] gap-6 p-6">
{menu.groups.map((group) => (
<div key={group.heading}>
<p className="text-muted-foreground font-mono text-xs tracking-[0.14em] uppercase">
{group.heading}
</p>
<ul className="mt-3 space-y-1">
{group.items.map((item) => (
<li key={item.title}>
<a
href="#"
className="hover:bg-muted focus-visible:ring-ring flex gap-3 rounded-md p-2 transition-colors focus-visible:ring-2 focus-visible:outline-none"
>
<item.icon
className="text-muted-foreground mt-0.5 size-4 shrink-0"
aria-hidden="true"
/>
<span className="min-w-0">
<span className="block text-sm font-medium">{item.title}</span>
<span className="text-muted-foreground block text-xs leading-snug">{item.body}</span>
</span>
</a>
</li>
))}
</ul>
</div>
))}
{/* Highlight rail: one promoted destination per panel, on the
muted surface so it reads as an aside, not a sixth link. */}
<div className="bg-muted/50 flex flex-col rounded-lg p-4">
<p className="text-sm font-semibold">{menu.highlight.title}</p>
<p className="text-muted-foreground mt-2 text-xs leading-relaxed">{menu.highlight.body}</p>
<Button variant="link" className="mt-auto h-auto justify-start p-0 pt-4 text-xs">
{menu.highlight.cta}
<ArrowRight className="ml-1 size-3" aria-hidden="true" />
</Button>
</div>
</div>
</NavigationMenuContent>
</NavigationMenuItem>
))}
{plainLinks.map((link) => (
<NavigationMenuItem key={link.href}>
<a href={link.href} className={navigationMenuTriggerStyle()}>
{link.label}
</a>
</NavigationMenuItem>
))}
</NavigationMenuList>
</NavigationMenu>
<div className="ml-auto flex items-center gap-2">
<Button variant="ghost" size="sm" className="hidden sm:inline-flex">
Sign in
</Button>
<Separator orientation="vertical" className="hidden h-5 sm:block" />
<Button size="sm">Book a demo</Button>
</div>
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="ml-auto lg:hidden" aria-label="Open navigation menu">
<Menu className="size-5" aria-hidden="true" />
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-72 overflow-y-auto">
<SheetHeader>
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<nav className="mt-6 grid gap-5" aria-label="Mobile">
{menus.map((menu) => (
<div key={menu.label}>
<p className="text-muted-foreground/70 text-xs font-medium tracking-wide uppercase">{menu.label}</p>
<ul className="mt-2 grid gap-1">
{menu.groups.map((group) =>
group.items.map((item) => (
<li key={item.title}>
<a
href="#"
className="hover:bg-muted block rounded-md px-3 py-2 text-sm font-medium transition-colors"
onClick={() => setOpen(false)}
>
{item.title}
</a>
</li>
)),
)}
</ul>
</div>
))}
<Separator />
{plainLinks.map((link) => (
<a
key={link.href}
href={link.href}
className="hover:bg-muted rounded-md px-3 py-2 text-sm font-medium transition-colors"
onClick={() => setOpen(false)}
>
{link.label}
</a>
))}
<Separator />
<div className="grid gap-2">
<Button variant="outline">Sign in</Button>
<Button>Book a demo</Button>
</div>
</nav>
</SheetContent>
</Sheet>
</div>
</header>
)
}
'use client'
import { useEffect, useState } from 'react'
import { BarChart3, BookOpen, Compass, FileText, History, Menu, Search, Users } from 'lucide-react'
import { Button } from '@/components/ui/button'
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command'
import { Kbd } from '@/components/ui/kbd'
import { Separator } from '@/components/ui/separator'
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
const links = [
{ label: 'Product', href: '#product' },
{ label: 'Pricing', href: '#pricing' },
{ label: 'Docs', href: '#docs' },
]
const groups = [
{
heading: 'Documentation',
items: [
{ icon: BookOpen, label: 'Connect a warehouse' },
{ icon: FileText, label: 'Define your first metric' },
{ icon: BarChart3, label: 'Publish a dashboard' },
],
},
{
heading: 'Company',
items: [
{ icon: Users, label: 'Customer stories' },
{ icon: History, label: 'Changelog' },
],
},
]
export function HeaderSearchCommand() {
const [open, setOpen] = useState(false)
const [menuOpen, setMenuOpen] = useState(false)
useEffect(() => {
const onKeydown = (event: KeyboardEvent) => {
if (event.key.toLowerCase() !== 'k' || !(event.metaKey || event.ctrlKey)) return
event.preventDefault()
setOpen((value) => !value)
}
window.addEventListener('keydown', onKeydown)
return () => window.removeEventListener('keydown', onKeydown)
}, [])
return (
<header data-slot="header-search-command" className="border-border bg-background/90 border-b backdrop-blur">
<div className="mx-auto flex h-16 max-w-6xl items-center gap-4 px-6">
<a href="#top" className="flex shrink-0 items-center gap-2 font-semibold tracking-tight">
<Compass className="text-primary size-5" aria-hidden="true" />
Halden
</a>
<nav className="hidden items-center gap-1 md:flex" aria-label="Primary">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className="text-muted-foreground hover:text-foreground hover:bg-muted focus-visible:ring-ring rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none"
>
{link.label}
</a>
))}
</nav>
{/* Trigger is a button, not an input: the real field lives in the dialog,
so focus lands there and the shortcut hint stays visible until then. */}
<button
type="button"
className="border-border bg-muted/40 text-muted-foreground hover:bg-muted focus-visible:ring-ring ml-auto flex h-9 w-full max-w-64 items-center gap-2 rounded-md border px-3 text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none"
onClick={() => setOpen(true)}
>
<Search className="size-4 shrink-0" aria-hidden="true" />
<span className="truncate">Search docs…</span>
<Kbd className="ml-auto hidden sm:inline-flex">⌘K</Kbd>
</button>
<Separator orientation="vertical" className="hidden h-5 md:block" />
<Button size="sm" className="hidden shrink-0 sm:inline-flex">
Start free
</Button>
<Sheet open={menuOpen} onOpenChange={setMenuOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="shrink-0 md:hidden" aria-label="Open navigation menu">
<Menu className="size-5" aria-hidden="true" />
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-72">
<SheetHeader>
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<nav className="mt-6 grid gap-1" aria-label="Mobile">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className="hover:bg-muted rounded-md px-3 py-2 text-sm font-medium transition-colors"
onClick={() => setMenuOpen(false)}
>
{link.label}
</a>
))}
</nav>
<Separator className="my-5" />
<div className="grid gap-2">
<Button variant="outline">Sign in</Button>
<Button>Start free</Button>
</div>
</SheetContent>
</Sheet>
</div>
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Search documentation, guides, and stories…" />
<CommandList>
<CommandEmpty>No results.</CommandEmpty>
{groups.map((group) => (
<CommandGroup key={group.heading} heading={group.heading}>
{group.items.map((item) => (
<CommandItem key={item.label} value={item.label}>
<item.icon className="text-muted-foreground mr-2 size-4" aria-hidden="true" />
{item.label}
</CommandItem>
))}
</CommandGroup>
))}
</CommandList>
</CommandDialog>
</header>
)
}
'use client'
import { useEffect, useState } from 'react'
import { Menu, Layers } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
const links = [
{ label: 'Product', href: '#product' },
{ label: 'Solutions', href: '#solutions' },
{ label: 'Pricing', href: '#pricing' },
{ label: 'Docs', href: '#docs' },
]
export function HeaderTransparentScroll() {
// Starts unscrolled so server and first client paint agree; the listener is
// the only thing that swaps in the solid surface.
const [scrolled, setScrolled] = useState(false)
const [open, setOpen] = useState(false)
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 24)
onScroll()
window.addEventListener('scroll', onScroll, { passive: true })
return () => window.removeEventListener('scroll', onScroll)
}, [])
return (
<header
data-slot="header-transparent-scroll"
className={`fixed inset-x-0 top-0 z-40 transition-colors duration-200 ${
scrolled ? 'border-border bg-background/85 border-b backdrop-blur' : 'border-b border-transparent'
}`}
>
<div className="mx-auto flex h-16 max-w-6xl items-center gap-6 px-6">
<a href="#top" className="flex items-center gap-2 font-semibold tracking-tight">
<Layers className="text-primary size-5" aria-hidden="true" />
Northwind
</a>
<nav className="hidden items-center gap-1 md:flex" aria-label="Primary">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className="text-muted-foreground hover:text-foreground hover:bg-muted focus-visible:ring-ring rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:outline-none"
>
{link.label}
</a>
))}
</nav>
<div className="ml-auto hidden items-center gap-2 md:flex">
<Button variant="ghost" size="sm">
Sign in
</Button>
<Button size="sm">Start free</Button>
</div>
<Sheet open={open} onOpenChange={setOpen}>
<SheetTrigger asChild>
<Button variant="ghost" size="icon" className="ml-auto md:hidden" aria-label="Open navigation menu">
<Menu className="size-5" aria-hidden="true" />
</Button>
</SheetTrigger>
<SheetContent side="right" className="w-72">
<SheetHeader>
<SheetTitle>Menu</SheetTitle>
</SheetHeader>
<nav className="mt-6 grid gap-1" aria-label="Mobile">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className="hover:bg-muted rounded-md px-3 py-2 text-sm font-medium transition-colors"
onClick={() => setOpen(false)}
>
{link.label}
</a>
))}
</nav>
<Separator className="my-5" />
<div className="grid gap-2">
<Button variant="outline">Sign in</Button>
<Button>Start free</Button>
</div>
</SheetContent>
</Sheet>
</div>
</header>
)
}
Raw manifest:https://uipkge.dev/r/react/header.json