@@ -4,8 +4,10 @@ import { Menu, X } from "lucide-react"
44import Link from "next/link"
55import { usePathname } from "next/navigation"
66import * as React from "react"
7+ import { createPortal } from "react-dom"
78
89import { Button } from "@/components/ui/button"
10+ import { useHeaderOffset } from "@/lib/hooks/useHeaderOffset"
911import { cn } from "@/lib/utils"
1012
1113const navigation = [
@@ -22,7 +24,9 @@ const navigation = [
2224export function Header ( ) {
2325 const [ mobileMenuOpen , setMobileMenuOpen ] = React . useState ( false )
2426 const [ scrolled , setScrolled ] = React . useState ( false )
27+ const [ mounted , setMounted ] = React . useState ( false )
2528 const pathname = usePathname ( )
29+ const headerOffset = useHeaderOffset ( ) ?? 64
2630
2731 React . useEffect ( ( ) => {
2832 const handleScroll = ( ) => {
@@ -32,6 +36,23 @@ export function Header() {
3236 return ( ) => window . removeEventListener ( "scroll" , handleScroll )
3337 } , [ ] )
3438
39+ React . useEffect ( ( ) => {
40+ setMounted ( true )
41+ } , [ ] )
42+
43+ React . useEffect ( ( ) => {
44+ if ( ! mobileMenuOpen ) {
45+ return
46+ }
47+
48+ const previousOverflow = document . body . style . overflow
49+ document . body . style . overflow = "hidden"
50+
51+ return ( ) => {
52+ document . body . style . overflow = previousOverflow
53+ }
54+ } , [ mobileMenuOpen ] )
55+
3556 return (
3657 < header
3758 className = { cn (
@@ -41,7 +62,7 @@ export function Header() {
4162 : "bg-transparent"
4263 ) }
4364 >
44- < nav className = "section-container flex items-center justify-between py-4" >
65+ < nav className = "section-container flex items-center justify-between py-4" data-header-height >
4566 < div className = "flex lg:flex-1" >
4667 < Link href = "/" className = "-m-1.5 p-1.5" >
4768 < span className = "text-xl font-bold tracking-tight" > Mathis Group</ span >
@@ -53,6 +74,8 @@ export function Header() {
5374 size = "icon"
5475 onClick = { ( ) => setMobileMenuOpen ( ! mobileMenuOpen ) }
5576 aria-label = "Toggle menu"
77+ aria-expanded = { mobileMenuOpen }
78+ aria-controls = "mobile-nav"
5679 >
5780 { mobileMenuOpen ? (
5881 < X className = "size-6" />
@@ -80,27 +103,34 @@ export function Header() {
80103 </ nav >
81104
82105 { /* Mobile menu */ }
83- { mobileMenuOpen && (
84- < div className = "lg:hidden" >
85- < div className = "space-y-1 border-t border-border p-6" >
86- { navigation . map ( ( item ) => (
87- < Link
88- key = { item . name }
89- href = { item . href }
90- className = { cn (
91- "block rounded-md px-3 py-2 text-base font-medium transition-colors hover:bg-accent" ,
92- pathname === item . href
93- ? "bg-accent text-foreground"
94- : "text-muted-foreground"
95- ) }
96- onClick = { ( ) => setMobileMenuOpen ( false ) }
97- >
98- { item . name }
99- </ Link >
100- ) ) }
101- </ div >
102- </ div >
103- ) }
106+ { mounted &&
107+ mobileMenuOpen &&
108+ createPortal (
109+ < div
110+ id = "mobile-nav"
111+ className = "fixed inset-x-0 bottom-0 z-50 overflow-y-auto border-t border-border bg-background/95 shadow-lg backdrop-blur supports-[backdrop-filter]:bg-background/80 lg:hidden"
112+ style = { { top : headerOffset } }
113+ >
114+ < div className = "section-container space-y-1 py-6" >
115+ { navigation . map ( ( item ) => (
116+ < Link
117+ key = { item . name }
118+ href = { item . href }
119+ className = { cn (
120+ "block rounded-md px-3 py-2 text-base font-medium transition-colors hover:bg-accent" ,
121+ pathname === item . href
122+ ? "bg-accent text-foreground"
123+ : "text-muted-foreground"
124+ ) }
125+ onClick = { ( ) => setMobileMenuOpen ( false ) }
126+ >
127+ { item . name }
128+ </ Link >
129+ ) ) }
130+ </ div >
131+ </ div > ,
132+ document . body
133+ ) }
104134 </ header >
105135 )
106136}
0 commit comments