SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
4.5 KB · 125 lines tsx
Raw Blame History
1"use client";2import * as React from "react";3import Link from "next/link";4import { usePathname } from "next/navigation";5import { AnimatePresence, motion, useReducedMotion } from "motion/react";6import { ArrowRight, Menu, X } from "lucide-react";7import { Logo } from "@/components/brand/logo";8import { Button } from "@/components/ui/button";9import { ThemeToggle } from "./theme-toggle";10import { cn } from "@/lib/utils";1112const NAV = [13  { label: "Features", href: "/#features" },14  { label: "Demo", href: "/#demo" },15  { label: "Models", href: "/models" },16  { label: "Security", href: "/security" },17  { label: "FAQ", href: "/#faq" },18];1920export function MarketingHeader() {21  const [open, setOpen] = React.useState(false);22  const pathname = usePathname();23  const reduce = useReducedMotion();2425  // Close the mobile menu when navigating (state adjusted during render, not in an effect).26  const [seenPath, setSeenPath] = React.useState(pathname);27  if (seenPath !== pathname) {28    setSeenPath(pathname);29    setOpen(false);30  }3132  React.useEffect(() => {33    if (!open) return;34    const onKey = (e: KeyboardEvent) => {35      if (e.key === "Escape") setOpen(false);36    };37    window.addEventListener("keydown", onKey);38    return () => window.removeEventListener("keydown", onKey);39  }, [open]);4041  return (42    <header className="glass sticky top-0 z-50 border-b border-border/80">43      <div className="mx-auto flex h-14 w-full max-w-6xl items-center justify-between gap-4 px-5 sm:px-8">44        <Link href="/" className="tap rounded-md py-1" aria-label="PolyLLM home">45          <Logo size={24} />46        </Link>4748        <nav className="hidden items-center gap-1 md:flex" aria-label="Primary">49          {NAV.map((item) => (50            <Link key={item.href} href={item.href} className="rounded-md px-3 py-1.5 text-sm text-fg-muted transition-colors hover:bg-bg-muted hover:text-fg">51              {item.label}52            </Link>53          ))}54        </nav>5556        <div className="hidden items-center gap-1.5 md:flex">57          <ThemeToggle />58          <Button asChild variant="ghost" size="sm">59            <Link href="/login">Sign in</Link>60          </Button>61          <Button asChild size="sm">62            <Link href="/signup">63              Start using PolyLLM64              <ArrowRight />65            </Link>66          </Button>67        </div>6869        <div className="flex items-center gap-1 md:hidden">70          <ThemeToggle />71          <button72            type="button"73            className="tap inline-flex size-8 items-center justify-center rounded-md text-fg-muted hover:bg-bg-muted hover:text-fg"74            aria-expanded={open}75            aria-controls="mobile-nav"76            aria-label={open ? "Close menu" : "Open menu"}77            onClick={() => setOpen((v) => !v)}78          >79            {open ? <X className="size-4.5" /> : <Menu className="size-4.5" />}80          </button>81        </div>82      </div>8384      <AnimatePresence initial={false}>85        {open ? (86          <motion.div87            id="mobile-nav"88            key="mobile-nav"89            className="overflow-hidden border-t border-border md:hidden"90            initial={reduce ? false : { height: 0, opacity: 0 }}91            animate={{ height: "auto", opacity: 1 }}92            exit={reduce ? undefined : { height: 0, opacity: 0 }}93            transition={{ duration: 0.22, ease: [0.16, 1, 0.3, 1] }}94          >95            <nav className="flex flex-col gap-1 px-4 py-3" aria-label="Mobile">96              {NAV.map((item) => (97                <Link98                  key={item.href}99                  href={item.href}100                  onClick={() => setOpen(false)}101                  className={cn("rounded-md px-3 py-2 text-[15px] text-fg-muted hover:bg-bg-muted hover:text-fg")}102                >103                  {item.label}104                </Link>105              ))}106              <div className="mt-2 grid grid-cols-2 gap-2 border-t border-border pt-3">107                <Button asChild variant="outline" size="lg" className="h-10 text-sm">108                  <Link href="/login" onClick={() => setOpen(false)}>109                    Sign in110                  </Link>111                </Button>112                <Button asChild size="lg" className="h-10 text-sm">113                  <Link href="/signup" onClick={() => setOpen(false)}>114                    Get started115                  </Link>116                </Button>117              </div>118            </nav>119          </motion.div>120        ) : null}121      </AnimatePresence>122    </header>123  );124}125