# Header Two

> A responsive marketing header with a bordered shell and animated mobile menu.

A shadcn/ui block from the [@navui](https://ui.navdeepsingh.dev) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/navui/header-two).

- Page, with a live preview: https://shadcn.exchange/r/navui/header-two
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/navui/header-two-light-90342a1583781597.png
- Registry item: https://ui.navdeepsingh.dev/r/header-two.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.navdeepsingh.dev/r/header-two.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: `button`

## Files

### `src/registry/blocks/header/header-two/header-two.tsx`

```tsx
'use client'
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { useState } from 'react'
import { Hexagon, PanelRight, X } from 'lucide-react'
import { AnimatePresence, motion, useReducedMotion } from 'motion/react'
import LogoMark from '@/components/ui-library-logo'

const navLinks = [
  { name: 'About', href: '#link' },
  { name: 'Features', href: '#link' },
  { name: 'Blog', href: '#link' },
  { name: 'Contact Us', href: '#link' },
]

export default function HeaderTwo() {
  return (
    <header className="relative w-full">
      <DesktopNavbar />
      <MobileNavbar />
    </header>
  )
}

function DesktopNavbar() {
  return (
    <nav className="sticky top-0 w-full bg-background">
      <div className="mx-auto hidden max-w-6xl items-center justify-between gap-6 px-4 py-3 md:flex">
        <div className="flex items-center gap-2">
          <LogoMark aria-hidden="true" />
          <span className="font-semibold text-foreground">Nav UI</span>
        </div>

        <div className="flex items-center justify-center gap-8">
          {navLinks.map((item) => (
            <Link
              href={item.href}
              key={item.name}
              className="text-sm font-medium text-muted-foreground transition-colors duration-200 hover:text-foreground"
            >
              {item.name}
            </Link>
          ))}
        </div>

        <div className="flex items-center gap-4">
          <Button
            className="border-b border-neutral-300 shadow-sm ring-1 ring-neutral-400"
            variant="default"
            asChild
          >
            <Link href="#login">Login</Link>
          </Button>
          <Button
            className="border-b-2 border-neutral-600 ring-1 ring-neutral-500"
            variant="outline"
            asChild
          >
            <Link href="#sign-up">Sign Up</Link>
          </Button>
        </div>
      </div>
    </nav>
  )
}

function MobileNavbar() {
  const [open, setOpen] = useState(false)
  const shouldReduceMotion = useReducedMotion()

  return (
    <nav className="relative z-50 flex justify-between bg-neutral-900 px-4 py-3 md:hidden">
      <div className="flex items-center gap-2">
        <Hexagon aria-hidden="true" className="size-6 fill-muted stroke-neutral-300 stroke-2" />
        <span className="font-semibold text-muted">Nav UI</span>
      </div>

      <button
        type="button"
        aria-label="Open navigation menu"
        aria-expanded={open}
        aria-controls="header-two-mobile-menu"
        onClick={() => {
          setOpen(!open)
        }}
      >
        <PanelRight aria-hidden="true" className="size-5 text-background open:hidden" />
      </button>

      <AnimatePresence>
        {open && (
          <motion.div
            id="header-two-mobile-menu"
            initial={
              shouldReduceMotion ? { opacity: 0 } : { opacity: 0, backdropFilter: 'blur(0px)' }
            }
            animate={{ opacity: 1, backdropFilter: 'blur(15px)' }}
            exit={
              shouldReduceMotion
                ? { opacity: 0 }
                : {
                    opacity: 0,
                    backdropFilter: 'blur(0px)',
                  }
            }
            transition={{
              duration: 0.2,
            }}
            className="absolute inset-0 z-50 h-full w-full px-4 py-2"
          >
            <div className="flex justify-between">
              <div className="flex items-center gap-2">
                <Hexagon
                  aria-hidden="true"
                  className="size-6 fill-muted stroke-neutral-300 stroke-2"
                />
                <span className="font-semibold text-background">Nav UI</span>
              </div>
              <button
                type="button"
                aria-label="Close navigation menu"
                onClick={() => setOpen(false)}
              >
                <X aria-hidden="true" className="absolute top-4 right-4 size-6 text-muted" />
              </button>
            </div>

            <div className="my-10 flex flex-col justify-center gap-6">
              {navLinks.map((item, index) => (
                <motion.div
                  key={item.name}
                  initial={shouldReduceMotion ? { opacity: 0 } : { opacity: 0, x: -4 }}
                  animate={{ opacity: 1, x: 0 }}
                  transition={{ duration: 0.2, delay: shouldReduceMotion ? 0 : index * 0.06 }}
                >
                  <Link
                    href={item.href}
                    onClick={() => setOpen(false)}
                    className="text-2xl font-medium text-neutral-400 transition-colors duration-200 hover:text-white dark:text-neutral-400"
                  >
                    {item.name}
                  </Link>
                </motion.div>
              ))}
            </div>
          </motion.div>
        )}
      </AnimatePresence>
    </nav>
  )
}
```

### `src/registry/components/ui-library-logo/ui-library-logo.tsx`

```tsx
import type { SVGProps } from 'react'

export type LogoProps = Omit<SVGProps<SVGSVGElement>, 'children'> & {
  title?: string
}

const MARK_PATH =
  'M48 230V76C48 46 66 29 90 29C110 29 122 42 136 61L250 211C263 228 279 232 299 232H319C347 232 366 213 366 188C366 164 352 149 331 142L279 123C255 114 244 98 244 77C244 50 264 30 291 30H366'

const MARK_TRANSFORM = 'translate(200 200) scale(.68) translate(-207 -130.5)'

export function Logo({ className, height = 22, title, width = 22, ...props }: LogoProps) {
  const classes = [
    '[--logo-start:#3F3F43]',
    '[--logo-highlight:#29292D]',
    '[--logo-middle:#141416]',
    '[--logo-end:#050506]',
    '[--logo-rim:rgba(255,255,255,0.28)]',
    '[--logo-mark:#FFFFFF]',

    'dark:[--logo-start:#FFFFFF]',
    'dark:[--logo-highlight:#FAFAFA]',
    'dark:[--logo-middle:#F0F0F1]',
    'dark:[--logo-end:#DEDEE2]',
    'dark:[--logo-rim:rgba(255,255,255,0.75)]',
    'dark:[--logo-mark:#18181B]',

    className,
  ]
    .filter(Boolean)
    .join(' ')

  return (
    <svg
      {...props}
      aria-hidden={title ? undefined : true}
      aria-label={title}
      className={classes}
      fill="none"
      focusable="false"
      height={height}
      preserveAspectRatio="xMidYMid meet"
      role={title ? 'img' : undefined}
      shapeRendering="geometricPrecision"
      viewBox="0 0 400 400"
      width={width}
      xmlns="http://www.w3.org/2000/svg"
    >
      {title ? <title>{title}</title> : null}

      <defs>
        <linearGradient
          id="logo-surface"
          x1="65"
          x2="335"
          y1="8"
          y2="392"
          gradientUnits="userSpaceOnUse"
        >
          <stop stopColor="var(--logo-start)" />
          <stop offset="0.22" stopColor="var(--logo-highlight)" />
          <stop offset="0.58" stopColor="var(--logo-middle)" />
          <stop offset="1" stopColor="var(--logo-end)" />
        </linearGradient>
      </defs>

      <rect
        fill="url(#logo-surface)"
        height="384"
        rx="88"
        stroke="var(--logo-rim)"
        strokeWidth="6"
        width="384"
        x="8"
        y="8"
      />

      <g transform={MARK_TRANSFORM}>
        <path
          d={MARK_PATH}
          stroke="var(--logo-mark)"
          strokeLinecap="round"
          strokeLinejoin="round"
          strokeWidth="32"
        />
      </g>
    </svg>
  )
}

export default Logo
```
