Skip to content

Commit 3bb9885

Browse files
authored
feat(header): improve mobile menu UX with overlay and transitions (#49)
* update dev_notes * ✨ feat: header (ui): improve mobile menu UX with overlay and transitions - File(s) changed: - src/components/Header.astro - Nature of changes: UX enhancement - Purpose: Make mobile menu behavior more natural and intuitive - Impact: Menu closes on outside click, backdrop blur focuses attention, smooth fade transitions, theme toggle closes menu
1 parent 7851612 commit 3bb9885

2 files changed

Lines changed: 48 additions & 39 deletions

File tree

dev_notes/backlog.md

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,35 +29,6 @@ Add MCP
2929

3030
https://docs.astro.build/en/guides/build-with-ai/
3131

32-
=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=
33-
34-
So when I build, sometimes I see these warnings.
35-
36-
What do I need to know ? Should I fix something about it?
37-
38-
--
39-
40-
Result (57 files):
41-
- 0 errors
42-
- 0 warnings
43-
- 0 hints
44-
45-
14:55:40 [content] Syncing content
46-
14:55:40 [content] Synced content
47-
14:55:40 [types] Generated 241ms
48-
14:55:40 [build] output: "static"
49-
14:55:40 [build] mode: "static"
50-
14:55:40 [build] directory: /Users/andy16/Documents/github_local/pascalandy-blog-paper/dist/
51-
14:55:40 [build] Collecting build info...
52-
14:55:40 [build] ✓ Completed in 250ms.
53-
14:55:40 [build] Building static entrypoints...
54-
14:55:41 [WARN] [vite] "matchHostname", "matchPathname", "matchPort" and "matchProtocol" are imported from external module "@astrojs/internal-helpers/remote" but never used in "node_modules/astro/dist/assets/utils/remotePattern.js" and "node_modules/astro/dist/assets/services/service.js".
55-
14:55:41 [assets] Copying fonts (12 files)...
56-
14:55:42 [vite] ✓ built in 1.57s
57-
14:55:42 [build] ✓ Completed in 1.60s.
58-
59-
60-
6132
=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=—=
6233

6334
Some posts have a section at the end called : "Share this post on:"

src/components/Header.astro

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,13 @@ const isActive = (path: string) => {
7474
<ul
7575
id="menu-items"
7676
class:list={[
77-
"hidden",
77+
"pointer-events-none opacity-0",
7878
"absolute top-full right-0 z-40 mt-2",
7979
"flex flex-col gap-1 p-4",
8080
"w-48 rounded-lg border border-border bg-background shadow-lg",
81+
"transition-opacity duration-150",
8182
"[&>li>a]:block [&>li>a]:p-3 [&>li>a]:font-medium [&>li>a]:hover:text-accent sm:[&>li>a]:px-2 sm:[&>li>a]:py-1",
82-
"sm:relative sm:top-auto sm:right-auto sm:z-auto sm:mt-0",
83+
"sm:pointer-events-auto sm:relative sm:top-auto sm:right-auto sm:z-auto sm:mt-0 sm:opacity-100",
8384
"sm:flex sm:w-auto sm:flex-row sm:items-center sm:gap-x-5 sm:gap-y-0",
8485
"sm:border-0 sm:bg-transparent sm:p-0 sm:shadow-none",
8586
]}
@@ -172,25 +173,62 @@ const isActive = (path: string) => {
172173
</div>
173174
</header>
174175

176+
<!-- Mobile menu backdrop overlay -->
177+
<div
178+
id="menu-overlay"
179+
class="pointer-events-none fixed inset-0 z-30 bg-background/60 opacity-0 backdrop-blur-sm transition-opacity duration-150 sm:hidden"
180+
aria-hidden="true"
181+
>
182+
</div>
183+
175184
<script>
176185
function toggleNav() {
177186
const menuBtn = document.querySelector("#menu-btn");
178187
const menuItems = document.querySelector("#menu-items");
179188
const menuIcon = document.querySelector("#menu-icon");
180189
const closeIcon = document.querySelector("#close-icon");
190+
const menuOverlay = document.querySelector("#menu-overlay");
181191

182-
if (!menuBtn || !menuItems || !menuIcon || !closeIcon) return;
192+
if (!menuBtn || !menuItems || !menuIcon || !closeIcon || !menuOverlay)
193+
return;
183194

184-
menuBtn.addEventListener("click", () => {
185-
const openMenu = menuBtn.getAttribute("aria-expanded") === "true";
195+
const openMenu = () => {
196+
menuBtn.setAttribute("aria-expanded", "true");
197+
menuBtn.setAttribute("aria-label", "Close Menu");
198+
menuItems.classList.remove("opacity-0", "pointer-events-none");
199+
menuItems.classList.add("opacity-100", "pointer-events-auto");
200+
menuOverlay.classList.remove("opacity-0", "pointer-events-none");
201+
menuOverlay.classList.add("opacity-100", "pointer-events-auto");
202+
menuIcon.classList.add("hidden");
203+
closeIcon.classList.remove("hidden");
204+
};
186205

187-
menuBtn.setAttribute("aria-expanded", openMenu ? "false" : "true");
188-
menuBtn.setAttribute("aria-label", openMenu ? "Open Menu" : "Close Menu");
206+
const closeMenu = () => {
207+
menuBtn.setAttribute("aria-expanded", "false");
208+
menuBtn.setAttribute("aria-label", "Open Menu");
209+
menuItems.classList.add("opacity-0", "pointer-events-none");
210+
menuItems.classList.remove("opacity-100", "pointer-events-auto");
211+
menuOverlay.classList.add("opacity-0", "pointer-events-none");
212+
menuOverlay.classList.remove("opacity-100", "pointer-events-auto");
213+
menuIcon.classList.remove("hidden");
214+
closeIcon.classList.add("hidden");
215+
};
189216

190-
menuItems.classList.toggle("hidden");
191-
menuIcon.classList.toggle("hidden");
192-
closeIcon.classList.toggle("hidden");
217+
menuBtn.addEventListener("click", () => {
218+
const isOpen = menuBtn.getAttribute("aria-expanded") === "true";
219+
if (isOpen) {
220+
closeMenu();
221+
} else {
222+
openMenu();
223+
}
193224
});
225+
226+
// Close menu when clicking overlay
227+
menuOverlay.addEventListener("click", closeMenu);
228+
229+
// Close menu when clicking theme toggle (doesn't navigate, so close manually)
230+
const themeBtn = document.querySelector("#theme-btn");
231+
themeBtn?.addEventListener("click", closeMenu);
194232
}
195233

196234
toggleNav();

0 commit comments

Comments
 (0)