Some checks are pending
Deploy to Cloudflare Workers (OpenNext) / deploy (push) Waiting to run
Use mainwhite.svg on white sections with curvy green transitions into flat green bands, improve text and button contrast, and deploy via OpenNext on Cloudflare Workers. Co-authored-by: Cursor <cursoragent@cursor.com>
172 lines
5.6 KiB
TypeScript
172 lines
5.6 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { Menu, X } from "lucide-react";
|
|
import { useEffect } from "react";
|
|
import {
|
|
Accordion,
|
|
AccordionContent,
|
|
AccordionItem,
|
|
AccordionTrigger,
|
|
} from "@/components/ui/accordion";
|
|
import { programDays } from "@/content/program";
|
|
import { site } from "@/content/site";
|
|
import { TicketsMarqueeCta } from "@/components/layout/NavTicketsCta";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
const navLinks = [
|
|
{ href: "/speakers", label: "Lineup" },
|
|
{ href: "/pitch-competition", label: "Pitch", badge: "Grants" },
|
|
{ href: "/partners", label: "Partners" },
|
|
{ href: "/exhibit", label: "Exhibit" },
|
|
] as const;
|
|
|
|
type TriggerProps = {
|
|
open: boolean;
|
|
onToggle: () => void;
|
|
};
|
|
|
|
export function MobileNavTrigger({ open, onToggle }: TriggerProps) {
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={onToggle}
|
|
className={cn(
|
|
"inline-flex items-center gap-2 rounded-full px-4 py-2.5 text-sm font-semibold transition-colors lg:hidden",
|
|
open
|
|
? "bg-[#141414] text-white"
|
|
: "bg-[#141414] text-white hover:bg-[#1f1f1f]"
|
|
)}
|
|
aria-expanded={open}
|
|
aria-controls="mobile-nav-panel"
|
|
aria-label={open ? "Close menu" : "Open menu"}
|
|
>
|
|
{open ? "Close" : "Menu"}
|
|
{open ? <X className="size-4" strokeWidth={2} /> : <Menu className="size-4" strokeWidth={2} />}
|
|
</button>
|
|
);
|
|
}
|
|
|
|
type DropdownProps = {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
};
|
|
|
|
export function MobileNavDropdown({ open, onClose }: DropdownProps) {
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const onKey = (e: KeyboardEvent) => {
|
|
if (e.key === "Escape") onClose();
|
|
};
|
|
document.body.style.overflow = "hidden";
|
|
window.addEventListener("keydown", onKey);
|
|
return () => {
|
|
document.body.style.overflow = "";
|
|
window.removeEventListener("keydown", onKey);
|
|
};
|
|
}, [open, onClose]);
|
|
|
|
if (!open) return null;
|
|
|
|
return (
|
|
<>
|
|
<button
|
|
type="button"
|
|
className="fixed inset-0 top-16 z-40 bg-black/30 lg:hidden"
|
|
aria-label="Close menu"
|
|
onClick={onClose}
|
|
/>
|
|
<nav
|
|
id="mobile-nav-panel"
|
|
className={cn(
|
|
"absolute left-0 right-0 top-[calc(100%+0.5rem)] z-50 overflow-hidden rounded-[1.75rem]",
|
|
"bg-[#141414] text-white shadow-2xl shadow-black/40",
|
|
"animate-in fade-in slide-in-from-top-3 duration-200"
|
|
)}
|
|
aria-label="Mobile navigation"
|
|
>
|
|
<div className="divide-y divide-white/10">
|
|
<Accordion type="single" collapsible className="w-full">
|
|
<AccordionItem value="program" className="border-0">
|
|
<AccordionTrigger
|
|
className={cn(
|
|
"px-5 py-5 text-lg font-medium text-white hover:no-underline",
|
|
"[&[data-state=open]>svg]:rotate-180",
|
|
"[&>svg]:size-5 [&>svg]:text-white/50"
|
|
)}
|
|
>
|
|
Program
|
|
</AccordionTrigger>
|
|
<AccordionContent className="px-5 pb-4 pt-0">
|
|
<ul className="space-y-1 border-t border-white/10 pt-3">
|
|
{programDays.map((day) => (
|
|
<li key={day.id}>
|
|
<Link
|
|
href="/program"
|
|
onClick={onClose}
|
|
className="block rounded-xl px-3 py-3 transition-colors hover:bg-white/5"
|
|
>
|
|
<span className="text-[10px] font-semibold uppercase tracking-wider text-white/45">
|
|
{day.date}
|
|
</span>
|
|
<span className="mt-0.5 block text-sm font-medium text-white/90">
|
|
{day.title}
|
|
</span>
|
|
</Link>
|
|
</li>
|
|
))}
|
|
<li>
|
|
<Link
|
|
href="/program"
|
|
onClick={onClose}
|
|
className="mt-1 block rounded-xl px-3 py-2.5 text-sm font-semibold text-[#ffb300] hover:bg-white/5"
|
|
>
|
|
View full program
|
|
</Link>
|
|
</li>
|
|
</ul>
|
|
</AccordionContent>
|
|
</AccordionItem>
|
|
</Accordion>
|
|
|
|
{navLinks.map((link) => (
|
|
<Link
|
|
key={link.href}
|
|
href={link.href}
|
|
onClick={onClose}
|
|
className="flex items-center justify-between px-5 py-5 text-lg font-medium transition-colors hover:bg-white/5"
|
|
>
|
|
<span>{link.label}</span>
|
|
{"badge" in link && link.badge && (
|
|
<span className="rounded-md bg-[#ffb300] px-2 py-0.5 text-[10px] font-bold uppercase tracking-wide text-[#0f0404]">
|
|
{link.badge}
|
|
</span>
|
|
)}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-3 border-t border-white/10 p-4">
|
|
<Link
|
|
href={site.links.pitchApplyUrl}
|
|
onClick={onClose}
|
|
className={cn(
|
|
"flex h-12 min-w-0 flex-1 items-center justify-center rounded-full",
|
|
"bg-white px-4 text-sm font-bold text-[#0f0404] transition-transform active:scale-[0.98]"
|
|
)}
|
|
>
|
|
Apply to pitch
|
|
</Link>
|
|
<TicketsMarqueeCta
|
|
onNavigate={onClose}
|
|
className={cn(
|
|
"h-12 min-w-[8.5rem] max-w-[9.5rem] shrink-0",
|
|
"focus-visible:ring-offset-[#141414]"
|
|
)}
|
|
/>
|
|
</div>
|
|
</nav>
|
|
</>
|
|
);
|
|
}
|