From 73a5596857a1307773a40f1ef418fc9da351b83c Mon Sep 17 00:00:00 2001 From: achmad Date: Fri, 29 May 2026 03:24:18 +0700 Subject: [PATCH] feat: mobile hamburger menu with full-screen overlay for categories --- components/layout/NavbarClient.tsx | 148 ++++++++++++++++++++++------- 1 file changed, 112 insertions(+), 36 deletions(-) diff --git a/components/layout/NavbarClient.tsx b/components/layout/NavbarClient.tsx index a3bb6bb..cee1bb1 100644 --- a/components/layout/NavbarClient.tsx +++ b/components/layout/NavbarClient.tsx @@ -1,8 +1,9 @@ 'use client' +import { useState, useEffect } from 'react' import Link from 'next/link' import { usePathname } from 'next/navigation' -import { Search } from 'lucide-react' +import { Search, Menu, X } from 'lucide-react' import type { Category } from '@/lib/types' interface Props { @@ -11,50 +12,125 @@ interface Props { export default function NavbarClient({ categories }: Props) { const pathname = usePathname() + const [menuOpen, setMenuOpen] = useState(false) + + useEffect(() => { + setMenuOpen(false) + }, [pathname]) + + useEffect(() => { + if (menuOpen) { + document.body.style.overflow = 'hidden' + } else { + document.body.style.overflow = '' + } + return () => { document.body.style.overflow = '' } + }, [menuOpen]) function openSearch() { window.dispatchEvent(new CustomEvent('kotobane:open-search')) } return ( - + {/* Mobile overlay */} + {menuOpen && ( +
+
setMenuOpen(false)} /> +
+ {/* Overlay header */} +
+ 言羽 + +
+ + {/* Category list */} +
+ {categories.map((cat) => { + const isActive = pathname.startsWith(`/${cat.slug}`) + return ( + + {cat.name} + {cat.description && ( + + {cat.description} + + )} + + ) + })} +
+
+
+ )} + ) }