# Header 2

A shadcn/ui block from the [@diarmuradi](https://diarmuradi.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/diarmuradi/header-2).

- Page, with a live preview: https://shadcn.exchange/r/diarmuradi/header-2
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/diarmuradi/header-2-light-407d247207851ac5.png
- Registry item: https://diarmuradi.com/r/header-2.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://diarmuradi.com/r/header-2.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`, `motion`
- Registry items: `https://diarmuradi.com/r/styles/base-nova/button.json`, `https://diarmuradi.com/r/styles/base-nova/icons.json`, `https://diarmuradi.com/r/styles/base-nova/separator.json`

## Files

### `header.tsx`

```tsx
"use client"

import { useEffect, useId, useRef, useState } from "react"
import Link from "next/link"
import { AnimatePresence, motion, useReducedMotion } from "motion/react"

import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
import { Icons } from "@/components/ui/icons"
import { XIcon } from 'lucide-react'

const NAV_LINKS = [
  { label: "Features", href: "#features" },
  { label: "Pricing", href: "#pricing" },
  { label: "About", href: "#about" },
  { label: "Blog", href: "#blog" },
]

export function SiteHeader() {
  const [isOpen, setIsOpen] = useState(false)

  const menuId = useId()
  const reducedMotion = useReducedMotion()
  const menuRef = useRef<HTMLElement>(null)
  useEffect(() => {
    if (!isOpen) return
    const dismiss = (event: PointerEvent | KeyboardEvent) => {
      if (event instanceof KeyboardEvent) {
        if (event.key !== "Escape") return
        event.preventDefault()
        setIsOpen(false)
        menuRef.current
          ?.querySelector<HTMLButtonElement>('button[aria-expanded="true"]')
          ?.focus()
      } else if (
        event.target instanceof Node &&
        !menuRef.current?.contains(event.target)
      ) {
        setIsOpen(false)
      }
    }
    const viewportWidth = window.innerWidth
    const resize = () => {
      if (window.innerWidth === viewportWidth) return
      setIsOpen(false)
    }
    document.addEventListener("pointerdown", dismiss)
    document.addEventListener("keydown", dismiss)
    window.addEventListener("resize", resize)
    return () => {
      document.removeEventListener("pointerdown", dismiss)
      document.removeEventListener("keydown", dismiss)
      window.removeEventListener("resize", resize)
    }
  }, [isOpen])

  return (
    <header
      ref={menuRef}
      className="sticky top-0 z-50 flex w-full justify-center px-4"
      data-slot="header"
    >
      <nav
        aria-label="Main navigation"
        className="flex w-full max-w-md flex-col gap-2 sm:h-14 sm:max-w-fit sm:flex-row sm:items-center sm:gap-2 sm:px-2"
      >
        <div className="flex h-14 w-full items-center justify-between sm:w-auto sm:shrink-0">
          <Button
            aria-label="Home"
            variant="ghost"
            size="icon-lg"

            render={<Link href="#" role="link" />}
            nativeButton={false}
          >
            <Icons.Logo aria-hidden="true" className="size-6 text-foreground" />
          </Button>

          <Button
            onClick={() => setIsOpen(!isOpen)}
            variant="ghost"
            className="size-10 rounded-xl text-foreground sm:hidden *:[&>svg]:size-5"
            aria-label={isOpen ? "Close Menu" : "Open Menu"}
            aria-expanded={isOpen}
            aria-controls={isOpen ? menuId : undefined}
          >
            {isOpen ? (
              <XIcon  aria-hidden="true" data-icon="inline-end" />
            ) : (
              <svg
                aria-hidden="true"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                strokeWidth={2}
                strokeLinecap="round"
                className="size-5"
              >
                <path d="M5 9h14M5 15h14" />
              </svg>
            )}
          </Button>
        </div>

        <div className="flex h-5 items-center justify-center max-sm:hidden">
          <Separator orientation="vertical" className="bg-border/50" />
        </div>

        <div className="group flex items-center gap-1 max-sm:hidden">
          {NAV_LINKS.map((link) => (
            <Button
              key={link.href}
              variant="ghost"
              className="text-muted-foreground group-has-[a:hover]:text-muted-foreground/50 hover:text-foreground hover:group-has-[a:hover]:text-foreground"
              render={<Link href={link.href} role="link" />}
              nativeButton={false}
            >
              {link.label}
            </Button>
          ))}
        </div>

        <div className="flex h-5 items-center justify-center max-sm:hidden">
          <Separator orientation="vertical" className="bg-border/50" />
        </div>

        <div className="flex shrink-0 items-center gap-1 max-sm:hidden">
          <Button
            variant="secondary"
            size="lg"
            className="h-10 rounded-l-full rounded-r-lg px-4"
            render={<Link href="#sign-up" role="link" />}
            nativeButton={false}
          >
            Sign up
          </Button>
          <Button
            size="lg"
            className="h-10 rounded-l-lg rounded-r-full px-4"
            render={<Link href="#sign-in" role="link" />}
            nativeButton={false}
          >
            Sign in
          </Button>
        </div>

        <AnimatePresence>
          {isOpen && (
            <motion.div
              data-slot="navigation-menu-popup"
              id={menuId}
              initial={{ opacity: 0, y: reducedMotion ? 0 : -8 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: reducedMotion ? 0 : -8 }}
              transition={{
                duration: reducedMotion ? 0 : 0.2,
                ease: "easeOut",
              }}
              className="flex w-full flex-col gap-2 rounded-3xl bg-popover p-2 shadow-elevated-sm sm:hidden"
            >
              <div className="flex flex-col gap-1">
                {NAV_LINKS.map((link) => (
                  <Button
                    key={link.href}
                    variant="ghost"
                    className="h-11 w-full justify-start rounded-2xl px-4 text-muted-foreground hover:bg-transparent hover:text-foreground"
                    onClick={() => setIsOpen(false)}
                    render={<Link href={link.href} role="link" />}
                    nativeButton={false}
                  >
                    {link.label}
                  </Button>
                ))}
              </div>

              <Separator className="bg-border/50" />

              <div className="grid grid-cols-2 gap-1">
                <Button
                  variant="secondary"
                  size="lg"
                  className="h-11 w-full min-w-0 rounded-l-2xl rounded-r-lg"
                  onClick={() => setIsOpen(false)}
                  render={<Link href="#sign-up" role="link" />}
                  nativeButton={false}
                >
                  Sign up
                </Button>
                <Button
                  size="lg"
                  className="h-11 w-full min-w-0 rounded-l-lg rounded-r-2xl"
                  onClick={() => setIsOpen(false)}
                  render={<Link href="#sign-in" role="link" />}
                  nativeButton={false}
                >
                  Sign in
                </Button>
              </div>
            </motion.div>
          )}
        </AnimatePresence>
      </nav>
    </header>
  )
}
```
