{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "promote-header", "type": "registry:ui", "title": "Promote Header", "description": "A promotional header component for announcements and marketing messages.", "dependencies": [ "lucide-react" ], "files": [ { "path": "registry/ruixenui/promote-header.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport Link from \"next/link\";\nimport { usePathname } from \"next/navigation\";\n\ntype NavItem = { href: string; label: string };\ntype Resource = {\n href: string;\n title: string;\n desc: string;\n icon: React.ReactNode;\n};\n\nconst NAV: NavItem[] = [\n { href: \"/\", label: \"Home\" },\n { href: \"/features\", label: \"Features\" },\n { href: \"/pricing\", label: \"Pricing\" },\n { href: \"/about\", label: \"About\" },\n];\n\nconst RESOURCES: Resource[] = [\n {\n href: \"/docs\",\n title: \"Docs\",\n desc: \"Guides, API, tutorials, and examples.\",\n icon: (\n \n \n \n \n ),\n },\n {\n href: \"/templates\",\n title: \"Templates\",\n desc: \"Production-ready starters for common use cases.\",\n icon: (\n \n \n \n ),\n },\n {\n href: \"/community\",\n title: \"Community\",\n desc: \"Join discussions and find support.\",\n icon: (\n \n \n \n \n \n ),\n },\n {\n href: \"/changelog\",\n title: \"Changelog\",\n desc: \"What shipped and when, with details.\",\n icon: (\n \n \n \n \n ),\n },\n];\n\nexport default function PromoteHeader() {\n const [pathname, setPathname] = React.useState(\"/\");\n const [open, setOpen] = React.useState(false);\n const [isDark, setIsDark] = React.useState(false);\n\n // Safely get pathname from Next.js hook\n let pathnameFromHook = null;\n try {\n pathnameFromHook = usePathname();\n } catch (error) {\n // Hook not available outside Next.js router context\n pathnameFromHook = null;\n }\n\n const activePath = pathnameFromHook ?? pathname;\n\n React.useEffect(() => {\n // Safely get pathname on client side\n if (typeof window !== \"undefined\") {\n setPathname(window.location.pathname);\n }\n }, []);\n\n React.useEffect(() => {\n // Initialize theme from localStorage or system\n const stored =\n typeof window !== \"undefined\" ? localStorage.getItem(\"theme\") : null;\n const systemDark =\n typeof window !== \"undefined\" &&\n window.matchMedia?.(\"(prefers-color-scheme: dark)\")?.matches;\n const dark = stored ? stored === \"dark\" : !!systemDark;\n document.documentElement.classList.toggle(\"dark\", dark);\n setIsDark(dark);\n }, []);\n\n const toggleTheme = () => {\n const next = !isDark;\n setIsDark(next);\n document.documentElement.classList.toggle(\"dark\", next);\n localStorage.setItem(\"theme\", next ? \"dark\" : \"light\");\n };\n\n const isActive = (href: string) =>\n href === \"/\" ? activePath === \"/\" : activePath?.startsWith(href);\n\n const primaryBtn =\n \"inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-semibold \" +\n \"bg-zinc-900 text-white hover:bg-zinc-900/90 \" +\n \"dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-zinc-100/90 \" +\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950/20 dark:focus-visible:ring-white/20\";\n\n const secondaryBtn =\n \"inline-flex h-9 items-center justify-center rounded-md px-3 text-sm font-medium \" +\n \"text-zinc-800 dark:text-zinc-200 \" +\n \"border border-zinc-950/20 dark:border-white/20 \" +\n \"hover:bg-zinc-950/[.03] dark:hover:bg-white/5 \" +\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950/20 dark:focus-visible:ring-white/20\";\n\n const desktopLink = (active: boolean) =>\n [\n \"rounded-md px-3 py-1.5 text-sm font-medium transition-colors\",\n \"outline-none ring-0 focus-visible:ring-2 focus-visible:ring-offset-0\",\n \"hover:bg-zinc-950/[.03] dark:hover:bg-white/5\",\n active\n ? \"text-zinc-950 dark:text-zinc-50 ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 bg-zinc-950/[.03] dark:bg-white/5\"\n : \"text-zinc-600 dark:text-zinc-400\",\n ].join(\" \");\n\n return (\n <>\n {/* Skip to content */}\n \n Skip to content\n \n\n {/* Announcement bar (optional) */}\n
\n
\n \n New\n \n \n Explore our updated templates—faster starters and better docs.\n \n \n Browse templates\n \n
\n
\n\n {/* Header */}\n \n {/* subtle gradient line */}\n
\n
\n
\n {/* Left: burger + brand + desktop nav */}\n
\n {/* Mobile burger */}\n setOpen((v) => !v)}\n className={[\n \"md:hidden group relative size-9 rounded-md\",\n \"text-zinc-700 hover:text-zinc-900 dark:text-zinc-300 dark:hover:text-zinc-100\",\n \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950/20 dark:focus-visible:ring-white/20\",\n ].join(\" \")}\n >\n \n \n \n \n\n {/* Brand */}\n \n \n Ruixen\n \n\n {/* Desktop Nav */}\n
\n
\n \n
\n\n {/* Right: search + theme + CTAs (desktop) */}\n
\n {/* search */}\n
\n \n \n \n \n \n \n /\n \n
\n\n {/* theme toggle */}\n \n {/* sun / moon swap without extra libs */}\n \n \n \n \n \n \n \n \n\n {/* actions */}\n \n Sign In\n \n \n Get Started\n \n
\n\n {/* Right (mobile): quick CTA */}\n
\n \n Start\n \n
\n
\n \n \n\n {/* Mobile drawer */}\n \n {/* overlay */}\n setOpen(false)}\n />\n\n {/* panel */}\n \n {/* header in drawer */}\n
\n setOpen(false)}\n className=\"flex items-center gap-2\"\n >\n \n Ruixen\n \n setOpen(false)}\n className=\"inline-flex size-9 items-center justify-center rounded-md border border-zinc-950/20 text-zinc-700 hover:bg-zinc-950/[.03] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-950/20 dark:border-white/20 dark:text-zinc-200 dark:hover:bg-white/5 dark:focus-visible:ring-white/20\"\n aria-label=\"Close menu\"\n >\n \n \n \n \n
\n\n {/* search */}\n
\n
\n \n \n \n \n \n
\n
\n\n {/* nav list */}\n \n\n {/* quick links */}\n
\n
\n Quick Links\n
\n
\n {RESOURCES.slice(0, 4).map((r) => (\n setOpen(false)}\n className=\"flex items-start gap-2 rounded-lg border border-zinc-950/20 p-2 text-sm hover:bg-zinc-950/[.03] dark:border-white/20 dark:hover:bg-white/5\"\n >\n \n {r.icon}\n \n \n {r.title}\n \n \n ))}\n
\n
\n\n {/* actions */}\n
\n setOpen(false)}\n className={secondaryBtn}\n >\n Sign In\n \n setOpen(false)}\n className={primaryBtn}\n >\n Get Started\n \n
\n\n {/* footer row in drawer */}\n
\n \n \n \n \n \n \n \n \n Toggle theme\n \n setOpen(false)}\n className=\"rounded-md border border-zinc-950/20 px-3 py-2 text-sm text-zinc-700 hover:bg-zinc-950/[.03] dark:border-white/20 dark:text-zinc-300 dark:hover:bg-white/5\"\n >\n Contact sales\n \n
\n \n \n \n );\n}\n", "type": "registry:ui", "target": "components/ruixen/promote-header.tsx" } ] }