From 1c12ab699b6215f7aafac60499a6f1fe99f94be9 Mon Sep 17 00:00:00 2001 From: Eugene Mutembei <103780583+CodeWithEugene@users.noreply.github.com> Date: Sun, 27 Sep 2026 10:57:33 +0300 Subject: [PATCH] fix(ui): unjam the mobile nav toggle Three defects combined into the 'jamming' hamburger on phones: - The actions row (theme, sign in, sign up, menu) plus the logo and two 16px grid gaps overflow a 360-375px viewport by ~30px, pushing the hamburger half off-screen into the OS edge-swipe zone. Below 767px the grid gaps tighten to 8px and the GitHub icon hides (still linked in the footer), so every control is fully on-screen again. - Fast double-taps reopened the drawer: the backdrop released pointer events the instant the close started, so the second tap hit the trigger under it. The overlay now keeps intercepting until its fade finishes (delayed visibility transition). - iOS scroll chaining: dragging the dimmed backdrop scrolled the page behind it, and scrolling the drawer rubber-banded the document. The overlay gets touch-action: none and the panel overscroll-behavior: contain. --- app/globals.css | 29 ++++++++++++++++++++++++++++- components/patterns/site-header.tsx | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/globals.css b/app/globals.css index 3c2eab5..76cdc21 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1095,12 +1095,25 @@ z-index: 70; background: color-mix(in srgb, var(--color-ink) 45%, transparent); opacity: 0; + visibility: hidden; pointer-events: none; - transition: opacity 220ms ease; + /* Touch gestures that start on the dimmed backdrop must not scroll the + page underneath; taps still fire, so click-to-close is unaffected. */ + touch-action: none; + /* visibility flips only after the fade finishes, so a fast second tap + lands on the backdrop (a no-op close) instead of the trigger below, + which used to instantly reopen the drawer — the "jamming" flicker. */ + transition: + opacity 220ms ease, + visibility 0s linear 220ms; } .mobile-nav-overlay-open { opacity: 1; + visibility: visible; pointer-events: auto; + transition: + opacity 220ms ease, + visibility 0s; } .mobile-nav-panel { position: fixed; @@ -1116,6 +1129,8 @@ transform: translateX(-100%); transition: transform 280ms cubic-bezier(0.65, 0, 0.35, 1); overflow-y: auto; + /* Scrolling the drawer must never chain into the page behind it. */ + overscroll-behavior: contain; } .mobile-nav-panel-open { transform: translateX(0); @@ -1175,6 +1190,18 @@ } @media (max-width: 767px) { + /* The actions row (theme, sign in, sign up, menu) plus the logo and two + 16px grid gaps overflow a 360–375px viewport by ~30px, pushing the + hamburger half off-screen into the OS edge-swipe zone — taps landed on + the bezel/back-gesture instead of the button. Tighten the gaps and + drop the GitHub icon (still linked in the footer and the drawer's + Contribute/Blog column) so every control is fully on-screen. */ + .site-header-grid { + gap: 8px; + } + .header-github { + display: none; + } .site-header-actions { gap: 2px; } diff --git a/components/patterns/site-header.tsx b/components/patterns/site-header.tsx index 837eed4..ccd1f19 100644 --- a/components/patterns/site-header.tsx +++ b/components/patterns/site-header.tsx @@ -61,7 +61,7 @@ export function SiteHeader({ className }: { className?: string }) { href="https://github.com/CodeWithEugene/HackVillage" target="_blank" rel="noopener noreferrer" - className="header-icon" + className="header-icon header-github" aria-label="HackVillage on GitHub (opens in a new tab)" >