diff --git a/components/Navbar.tsx b/components/Navbar.tsx index 600b51a..94b92e3 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { Menu, X } from 'lucide-react'; @@ -21,6 +21,8 @@ const NAV = [ export function Navbar() { const path = usePathname(); const [menuOpen, setMenuOpen] = useState(false); + const mobileMenuRef = useRef(null); + const previousFocusRef = useRef(null); const network = useSelectedNetwork(); // Close the mobile menu whenever the route changes (e.g. after a nav tap). @@ -28,11 +30,38 @@ export function Navbar() { setMenuOpen(false); }, [path]); - // Close on Escape for keyboard users. + // Move focus into the drawer, trap it there, and return it to the trigger + // when closed. This makes the visual drawer usable without a mouse. useEffect(() => { - if (!menuOpen) return; + if (!menuOpen) { + previousFocusRef.current?.focus(); + previousFocusRef.current = null; + return; + } + + previousFocusRef.current = document.activeElement instanceof HTMLElement + ? document.activeElement + : null; + const focusableSelector = 'a[href], button:not([disabled])'; + const focusable = () => Array.from( + mobileMenuRef.current?.querySelectorAll(focusableSelector) ?? [], + ); + requestAnimationFrame(() => focusable()[0]?.focus()); + const onKey = (e: KeyboardEvent) => { if (e.key === 'Escape') setMenuOpen(false); + if (e.key !== 'Tab') return; + const items = focusable(); + if (items.length === 0) return; + const first = items[0]!; + const last = items[items.length - 1]!; + if (e.shiftKey && document.activeElement === first) { + e.preventDefault(); + last.focus(); + } else if (!e.shiftKey && document.activeElement === last) { + e.preventDefault(); + first.focus(); + } }; document.addEventListener('keydown', onKey); return () => document.removeEventListener('keydown', onKey); @@ -61,7 +90,7 @@ export function Navbar() { {/* Desktop nav links */} -