SPB Git forge
38commits 1branches 0releases
338.7 MBsize
maindefault branch
4 h agolast push
HTML 53.9% TypeScript 44.5% JavaScript 0.6% SQL 0.5%
2.5 KB · 57 lines tsx
Raw Blame History
1"use client";23import { Activity, Map as MapIcon, Radio, Search, Trophy } from "lucide-react";4import Link from "next/link";5import { usePathname } from "next/navigation";6import { openPalette } from "@/components/command-palette";7import { cn } from "@/components/ui/cn";8import { routes } from "@/lib/routes";910/** §72 — Map · Live · Projects · Rank · Search. */11const ITEMS = [12  { label: "Map", href: routes.map(), icon: MapIcon, match: (p: string) => p.startsWith("/map") },13  { label: "Live", href: routes.live(), icon: Radio, match: (p: string) => p.startsWith("/live") || p.startsWith("/pulse"), live: true },14  { label: "Projects", href: routes.projects(), icon: Activity, match: (p: string) => p.startsWith("/projects") },15  { label: "Rank", href: routes.rankings(), icon: Trophy, match: (p: string) => p.startsWith("/rankings") },16  { label: "Search", href: null, icon: Search, match: (p: string) => p.startsWith("/search") },17];1819/** Fixed bottom nav (mobile only). Hidden by `[data-hide-mobile-nav]` on body (map page sheets). */20export function MobileNav() {21  const path = usePathname();22  return (23    <nav aria-label="Mobile" className="fixed inset-x-0 bottom-0 z-[55] border-t border-hair bg-paper/95 backdrop-blur safe-b md:hidden [body[data-hide-mobile-nav]_&]:hidden">24      <ul className="grid h-14 grid-cols-5">25        {ITEMS.map((it) => {26          const active = it.match(path);27          const Icon = it.icon;28          const cls = cn("relative flex h-full flex-col items-center justify-center gap-0.5 text-[10.5px] font-medium", active ? "text-ink" : "text-ink-3");29          const inner = (30            <>31              <span className="relative">32                <Icon className="size-5" strokeWidth={active ? 2 : 1.6} />33                {it.live && <span aria-hidden className="absolute -right-1 -top-0.5 inline-block size-1.5 rounded-full bg-accent animate-pulse-soft" />}34              </span>35              <span>{it.label}</span>36              {active && <span aria-hidden className="absolute inset-x-5 top-0 h-[2px] bg-ink" />}37            </>38          );39          return (40            <li key={it.label}>41              {it.href ? (42                <Link href={it.href} aria-current={active ? "page" : undefined} className={cls}>43                  {inner}44                </Link>45              ) : (46                <button type="button" onClick={() => openPalette()} className={cn(cls, "w-full")} aria-label="Search">47                  {inner}48                </button>49              )}50            </li>51          );52        })}53      </ul>54    </nav>55  );56}57