From ba675c0876707a529a99858f438fb65b804a6f88 Mon Sep 17 00:00:00 2001 From: Michelle Date: Fri, 26 Dec 2025 01:39:57 +0400 Subject: [PATCH] refactor: restructure mobile nav to use Sheet subcomponents MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mobile Nav: - Adopt SheetHeader/SheetFooter to mirror sidebar's layout pattern - Increase UserButton avatar size and auth button text - Simplify comments Fixes: - useIsMobile defaults to false to prevent hydration mismatch - NavLink padding reduced (px-4 → px-3) - Star icon viewBox adjusted for proper 20x20 sizing Aligns mobile nav structure with sidebar conventions for consistency. The hydration fix ensures server/client markup matches on initial render. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- components/navigation/mobile-nav.tsx | 57 +++++++++++++--------------- components/navigation/nav-link.tsx | 7 +--- hooks/use-mobile.ts | 7 ++-- public/icons/star.svg | 2 +- 4 files changed, 32 insertions(+), 41 deletions(-) diff --git a/components/navigation/mobile-nav.tsx b/components/navigation/mobile-nav.tsx index ebf6fd0..3cf6348 100644 --- a/components/navigation/mobile-nav.tsx +++ b/components/navigation/mobile-nav.tsx @@ -18,7 +18,8 @@ import { Sheet, SheetClose, SheetContent, - SheetDescription, + SheetFooter, + SheetHeader, SheetTitle, SheetTrigger, } from "@/components/ui/sheet"; @@ -31,8 +32,8 @@ export function MobileNav() { return ( <> - {/* Tap-to-dismiss overlay: modal={false} allows Clerk popups to work (they render - outside Sheet), but disables SheetOverlay dismiss. See authenticated.mobile.spec.ts */} + {/* modal={false} allows Clerk popups to work (they render outside Sheet), but + disables SheetOverlay dismiss. See authenticated.mobile.spec.ts */} {open && ( - - + + diff --git a/components/navigation/nav-link.tsx b/components/navigation/nav-link.tsx index 3ab89dd..dccf292 100644 --- a/components/navigation/nav-link.tsx +++ b/components/navigation/nav-link.tsx @@ -17,10 +17,7 @@ type NavLinkProps = NavLinkType & { onClick?: () => void; }; -/** - * Mobile navigation link for Sheet menu. - * Desktop sidebar uses SidebarMenuButton directly in AppSidebar. - */ +/** Navigation link for mobile Sheet menu. */ export function NavLink({ imgURL, route, label, onClick }: NavLinkProps) { const pathname = usePathname(); const isActive = isRouteActive(pathname, route); @@ -30,7 +27,7 @@ export function NavLink({ imgURL, route, label, onClick }: NavLinkProps) { href={route} onClick={onClick} className={cn( - "flex items-center gap-3 rounded-lg px-4 py-3", + "flex items-center gap-3 rounded-lg px-3 py-3", isActive ? NAV_LINK_ACTIVE_CLASSES : `${NAV_LINK_INACTIVE_CLASSES} text-sidebar-foreground hover:bg-muted`, diff --git a/hooks/use-mobile.ts b/hooks/use-mobile.ts index 97770e2..7365b7d 100644 --- a/hooks/use-mobile.ts +++ b/hooks/use-mobile.ts @@ -3,9 +3,8 @@ import * as React from "react"; const MOBILE_BREAKPOINT = 640; // tailwind sm breakpoint export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState( - undefined, - ); + // Start with false to match server-rendered HTML and prevent hydration mismatch + const [isMobile, setIsMobile] = React.useState(false); React.useEffect(() => { const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`); @@ -17,5 +16,5 @@ export function useIsMobile() { return () => mql.removeEventListener("change", onChange); }, []); - return !!isMobile; + return isMobile; } diff --git a/public/icons/star.svg b/public/icons/star.svg index 0a565af..ce5ce67 100644 --- a/public/icons/star.svg +++ b/public/icons/star.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file