summaryrefslogtreecommitdiff
path: root/apps/web/src/components/header.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'apps/web/src/components/header.tsx')
-rw-r--r--apps/web/src/components/header.tsx180
1 files changed, 135 insertions, 45 deletions
diff --git a/apps/web/src/components/header.tsx b/apps/web/src/components/header.tsx
index f8877330..95f08304 100644
--- a/apps/web/src/components/header.tsx
+++ b/apps/web/src/components/header.tsx
@@ -1,57 +1,147 @@
"use client";
+import { useState } from "react";
import Link from "next/link";
+import { motion } from "motion/react";
import { Button } from "./ui/button";
import { ArrowRight } from "lucide-react";
-import { HeaderBase } from "./header-base";
import Image from "next/image";
import { ThemeToggle } from "./theme-toggle";
+import { GithubIcon, Menu02Icon } from "@hugeicons/core-free-icons";
+import { HugeiconsIcon } from "@hugeicons/react";
+import { cn } from "@/utils/ui";
+import { DEFAULT_LOGO_URL, SOCIAL_LINKS } from "@/constants/site-constants";
export function Header() {
- const leftContent = (
- <Link href="/" className="flex items-center gap-3">
- <Image
- src="/logo.svg"
- alt="OpenCut Logo"
- className="invert dark:invert-0"
- width={32}
- height={32}
- />
- <span className="text-xl font-medium hidden md:block">OpenCut</span>
- </Link>
- );
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const closeMenu = () => setIsMenuOpen(false);
- const rightContent = (
- <nav className="flex items-center gap-2">
- <div className="flex items-center gap-4">
- <Link href="/blog">
- <Button variant="text" className="text-sm p-0">
- Blog
- </Button>
- </Link>
- <Link href="/contributors">
- <Button variant="text" className="text-sm p-0">
- Contributors
- </Button>
- </Link>
- </div>
- <Link href="/projects">
- <Button size="sm" className="text-sm ml-2">
- Projects
- <ArrowRight className="h-4 w-4" />
- </Button>
- </Link>
- <ThemeToggle className="mr-2" />
- </nav>
- );
+ const links = [
+ {
+ label: "Contributors",
+ href: "/contributors",
+ },
+ {
+ label: "Sponsors",
+ href: "/sponsors",
+ },
+ {
+ label: "Blog",
+ href: "/blog",
+ },
+ ];
- return (
- <div className="sticky top-4 z-50 mx-4 md:mx-0">
- <HeaderBase
- className="bg-background border rounded-2xl max-w-3xl mx-auto mt-4 pl-4 pr-[11px]"
- leftContent={leftContent}
- rightContent={rightContent}
- />
- </div>
- );
+ return (
+ <header className="bg-background shadow-background/85 sticky top-0 z-10 shadow-[0_30px_35px_15px_rgba(0,0,0,1)]">
+ <div className="relative flex w-full items-center justify-between px-6 pt-4">
+ <div className="relative z-10 flex items-center gap-6">
+ <Link href="/" className="flex items-center gap-3">
+ <Image
+ src={DEFAULT_LOGO_URL}
+ alt="OpenCut Logo"
+ className="invert dark:invert-0"
+ width={32}
+ height={32}
+ />
+ </Link>
+ <nav className="hidden items-center gap-4 md:flex">
+ {links.map((link) => (
+ <Link key={link.href} href={link.href}>
+ <Button variant="text" className="p-0 text-sm">
+ {link.label}
+ </Button>
+ </Link>
+ ))}
+ </nav>
+ </div>
+
+ <div className="relative z-10">
+ <div className="flex items-center gap-3 md:hidden">
+ <Button
+ variant="text"
+ size="icon"
+ className="flex items-center justify-center p-0"
+ onClick={() => setIsMenuOpen(!isMenuOpen)}
+ >
+ <HugeiconsIcon icon={Menu02Icon} size={30} />
+ </Button>
+ </div>
+ <div className="hidden items-center gap-3 md:flex">
+ <Link href={SOCIAL_LINKS.github}>
+ <Button className="bg-background text-sm" variant="outline">
+ <HugeiconsIcon icon={GithubIcon} className="size-4" />
+ 40k+
+ </Button>
+ </Link>
+ <Link href="/projects">
+ <Button variant="foreground" className="text-sm">
+ Projects
+ <ArrowRight className="size-4" />
+ </Button>
+ </Link>
+ <ThemeToggle />
+ </div>
+ </div>
+ <div
+ className={cn(
+ "bg-background/20 pointer-events-none fixed inset-0 opacity-0 backdrop-blur-3xl",
+ "transition-opacity duration-150",
+ isMenuOpen && "pointer-events-auto opacity-100",
+ )}
+ >
+ <div className="relative h-full">
+ <button
+ type="button"
+ aria-label="Close menu"
+ className="absolute inset-0"
+ onClick={closeMenu}
+ onKeyDown={(event) => {
+ if (
+ event.key === "Enter" ||
+ event.key === " " ||
+ event.key === "Escape"
+ ) {
+ event.preventDefault();
+ closeMenu();
+ }
+ }}
+ />
+ <nav className="flex flex-col gap-3 px-6 pt-[5rem]">
+ {links.map((link, index) => (
+ <motion.div
+ key={link.href}
+ initial={{ scale: 0.98, opacity: 0 }}
+ animate={{
+ scale: isMenuOpen ? 1 : 0.98,
+ opacity: isMenuOpen ? 1 : 0,
+ }}
+ transition={{
+ duration: 0.4,
+ delay: isMenuOpen ? index * 0.1 : 0,
+ ease: [0.25, 0.46, 0.45, 0.94],
+ }}
+ >
+ <Link
+ href={link.href}
+ className="text-2xl font-semibold"
+ onClick={() => setIsMenuOpen(false)}
+ >
+ {link.label}
+ </Link>
+ </motion.div>
+ ))}
+ </nav>
+ <ThemeToggle
+ className="absolute right-8 bottom-8 size-10"
+ iconClassName="!size-[1.2rem]"
+ onToggle={(e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ }}
+ />
+ </div>
+ </div>
+ </div>
+ </header>
+ );
}