Site Header Narrow
atroui/site-header-narrowFreeBlock
Sticky 640px personal header with mono nav and theme icon.
Install it
npx shadcn@latest add https://atroui.com/r/site-header-narrow.jsonSource
1"use client"23import * as React from "react"4import Link from "next/link"5import { usePathname } from "next/navigation"6import { Menu, Moon, Sun } from "lucide-react"7import { useTheme } from "next-themes"89import { getBrand } from "@/lib/brand"10import { cn } from "@/lib/utils"1112const CONTENT = {13 siteName: "",14 primaryNav: [15 { href: "/projects", label: "projects" },16 { href: "/writing", label: "writing" },17 { href: "/resume", label: "resume" },18 ] as Array<{ href: string; label: string; external?: boolean }>,19 moreNav: [20 { href: "/log", label: "log" },21 { href: "/stack", label: "stack" },22 { href: "/contact", label: "contact" },23 ] as Array<{ href: string; label: string }>,24}2526/** Inlined icon toggle so this block stays installable alone. */27function ThemeToggleIcon({ className }: { className?: string }) {28 const { resolvedTheme, setTheme } = useTheme()29 const [mounted, setMounted] = React.useState(false)30 React.useEffect(() => setMounted(true), [])3132 const isDark = mounted && resolvedTheme === "dark"33 const next = isDark ? "light" : "dark"34 const label = `Switch to ${next} mode`3536 return (37 <button38 type="button"39 aria-label={label}40 title={label}41 onClick={() => setTheme(next)}42 className={cn(43 "inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",44 className45 )}46 >47 {!mounted ? (48 <Sun className="h-[13px] w-[13px] opacity-0" aria-hidden />49 ) : isDark ? (50 <Sun className="h-[13px] w-[13px]" aria-hidden />51 ) : (52 <Moon className="h-[13px] w-[13px]" aria-hidden />53 )}54 </button>55 )56}5758export function SiteHeaderNarrow({59 siteName,60 primaryNav = CONTENT.primaryNav,61 moreNav = CONTENT.moreNav,62 onOpenCommand,63 className,64}: {65 siteName?: string66 primaryNav?: Array<{ href: string; label: string; external?: boolean }>67 moreNav?: Array<{ href: string; label: string }>68 onOpenCommand?: () => void69 className?: string70} = {}) {71 const pathname = usePathname()72 const [scrolled, setScrolled] = React.useState(false)73 const wordmark = siteName || CONTENT.siteName || getBrand().name7475 React.useEffect(() => {76 const onScroll = () => setScrolled(window.scrollY > 4)77 onScroll()78 window.addEventListener("scroll", onScroll, { passive: true })79 return () => window.removeEventListener("scroll", onScroll)80 }, [])8182 return (83 <header84 className={cn(85// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from atroui
All 82 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Analytics Provideranalytics-provider | atroui | Blocks | Free | 1 dep | |
| AR Portfolioar-portfolio | atroui | Blocks | Free | 1 dep | |
| Before / Afterbefore-after-slider | atroui | Blocks | Free | no deps | |
| Calendly Embedcalendly-embed | atroui | Blocks | Free | 1 dep | |
| Changelogchangelog | atroui | Blocks | Free | no deps | |
| Command Menucommand-menu | atroui | Blocks | Free | 4 deps | |
| Contact Formcontact-form | atroui | Blocks | Free | 2 deps | |
| Contextual CTAcontextual-cta | atroui | Blocks | Free | 2 deps |
