Skip to content

Commit 68af847

Browse files
Improve mobile menu UX: unified toggle button, larger touch targets, better spacing
Amp-Thread-ID: https://ampcode.com/threads/T-688cbfe7-b494-4c8b-ae93-eab9ef729050 Co-authored-by: Amp <amp@ampcode.com>
1 parent 25aa9c9 commit 68af847

1 file changed

Lines changed: 52 additions & 30 deletions

File tree

src/components/Navigation.astro

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,37 +38,36 @@ const NAVIGATION_ITEMS = [
3838
</ul>
3939

4040
<button
41-
class="flex sm:hidden"
42-
onclick="toggleMobileMenu()"
41+
type="button"
4342
id="mobile-menu-button"
43+
class="sm:hidden inline-flex items-center justify-center w-11 h-11 rounded-md text-gray-900 hover:text-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-600/50 focus-visible:ring-offset-2 relative z-50"
44+
aria-controls="mobile-menu"
45+
aria-expanded="false"
46+
aria-label="Open menu"
47+
onclick="toggleMobileMenu()"
4448
>
45-
<svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
49+
<svg id="icon-hamburger" class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
4650
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
4751
</svg>
52+
<svg id="icon-close" class="h-6 w-6 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
53+
<line x1="22" y1="2" x2="2" y2="22" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
54+
<line x1="2" y1="2" x2="22" y2="22" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
55+
</svg>
4856
</button>
49-
<div id="mobile-menu" class="hide-menu-nav">
50-
<div class="absolute top-0 right-0 mt-1 p-4" onclick="closeMobileMenu()">
51-
<svg
52-
class="h-8 w-8 text-gray-600"
53-
viewBox="0 0 24 24"
54-
fill="none"
55-
stroke="currentColor"
56-
stroke-width="2"
57-
stroke-linecap="round"
58-
stroke-linejoin="round"
59-
>
60-
<line x1="22" y1="2" x2="2" y2="22" />
61-
<line x1="2" y1="2" x2="22" y2="22" />
62-
</svg>
63-
</div>
64-
<ul class="flex flex-col items-center justify-between min-h-[150px]">
57+
<div
58+
id="mobile-menu"
59+
class="hide-menu-nav fixed inset-0 z-40 bg-white"
60+
role="dialog"
61+
aria-modal="true"
62+
>
63+
<ul class="flex flex-col w-full pt-20 divide-y divide-gray-200">
6564
{NAVIGATION_ITEMS.map((item) => (
66-
<li>
65+
<li class="w-full">
6766
<a
6867
href={item.link}
6968
onclick="closeMobileMenu()"
70-
class={`font-light uppercase hover:text-indigo-600 my-8 border-indigo-600 transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-600/50 focus-visible:ring-offset-2 rounded-sm ${
71-
normalizedCurrentPath === item.link ? 'text-indigo-600 border-b-2 pb-2' : ''
69+
class={`block w-full px-6 py-4 text-lg font-medium text-gray-900 hover:bg-gray-50 transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-600/50 focus-visible:ring-offset-2 ${
70+
normalizedCurrentPath === item.link ? 'text-indigo-600 bg-indigo-50' : ''
7271
}`}
7372
>
7473
{item.title}
@@ -102,22 +101,45 @@ const NAVIGATION_ITEMS = [
102101
</style>
103102

104103
<script>
105-
function toggleMobileMenu() {
104+
function setMenuState(open: boolean) {
106105
const menu = document.getElementById('mobile-menu');
107-
if (menu?.classList.contains('hide-menu-nav')) {
108-
menu.classList.remove('hide-menu-nav');
109-
menu.classList.add('show-menu-nav');
106+
const btn = document.getElementById('mobile-menu-button');
107+
const iconOpen = document.getElementById('icon-hamburger');
108+
const iconClose = document.getElementById('icon-close');
109+
110+
if (open) {
111+
menu?.classList.remove('hide-menu-nav');
112+
menu?.classList.add('show-menu-nav');
113+
btn?.setAttribute('aria-expanded', 'true');
114+
btn?.setAttribute('aria-label', 'Close menu');
115+
iconOpen?.classList.add('hidden');
116+
iconClose?.classList.remove('hidden');
117+
document.body.classList.add('overflow-hidden');
110118
} else {
111-
closeMobileMenu();
119+
menu?.classList.remove('show-menu-nav');
120+
menu?.classList.add('hide-menu-nav');
121+
btn?.setAttribute('aria-expanded', 'false');
122+
btn?.setAttribute('aria-label', 'Open menu');
123+
iconOpen?.classList.remove('hidden');
124+
iconClose?.classList.add('hidden');
125+
document.body.classList.remove('overflow-hidden');
112126
}
113127
}
114128

115-
function closeMobileMenu() {
129+
function toggleMobileMenu() {
116130
const menu = document.getElementById('mobile-menu');
117-
menu?.classList.remove('show-menu-nav');
118-
menu?.classList.add('hide-menu-nav');
131+
const opening = menu?.classList.contains('hide-menu-nav');
132+
setMenuState(!!opening);
133+
}
134+
135+
function closeMobileMenu() {
136+
setMenuState(false);
119137
}
120138

139+
document.addEventListener('keydown', (e) => {
140+
if (e.key === 'Escape') closeMobileMenu();
141+
});
142+
121143
// @ts-ignore
122144
window.toggleMobileMenu = toggleMobileMenu;
123145
// @ts-ignore

0 commit comments

Comments
 (0)