fix(website): portal mobile nav sheet to body so it covers the viewport#670
fix(website): portal mobile nav sheet to body so it covers the viewport#670ambicuity wants to merge 1 commit into
Conversation
The mobile nav sheet was rendered inside the <header> element, which uses backdrop-filter: blur(10px). Per CSS spec, backdrop-filter establishes a containing block for fixed-positioned descendants, so the sheet's `position: fixed; inset: 0` was clipped to the header strip instead of covering the viewport. The dark overlay only painted across the top bar, leaving the hero fully visible behind the menu and the panel items squeezed into a strip too short to render. Render the sheet through createPortal to document.body so it escapes the nav's containing block. Drop backdrop-filter on the sheet (no longer needed once the bg is fully opaque) and switch to var(--abyss) so hero content can't bleed through even if a browser ignores 0.94 opacity. Closes rohitg00#669
|
@ambicuity is attempting to deploy a commit to the rohitg00's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughMobileNavToggle now renders the mobile navigation sheet via a React portal to ChangesMobile Nav Sheet Portal Migration
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR updates the mobile navigation sheet to render via a React portal so it’s not clipped by the header’s backdrop-filter containing block.
Changes:
- Portal the mobile nav sheet to
document.bodyusingcreatePortaland a client-onlymountedguard - Adjust the sheet background styling and add documentation comments about stacking context
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| website/components/MobileNavToggle.tsx | Portals the mobile sheet to <body> to avoid clipping and adds a mount guard for client-only DOM access. |
| website/components/MobileNavToggle.module.css | Updates sheet background and documents z-index/stacking context expectations for the portaled overlay. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| className={`${styles.sheet} ${open ? styles.sheetOpen : ""}`} | ||
| aria-hidden={!open} |
| <nav className={styles.panel} aria-label="Site navigation"> | ||
| <ul className={styles.list}> | ||
| {sections.map((s) => ( | ||
| <li key={s.href}> | ||
| <a href={s.href} onClick={() => setOpen(false)}> | ||
| {s.label} | ||
| </a> |
| </div> | ||
| </nav> | ||
| </div> | ||
| {mounted ? createPortal(sheet, document.body) : null} |
| <a | ||
| href="https://github.com/rohitg00/agentmemory" | ||
| target="_blank" | ||
| rel="noopener" | ||
| onClick={() => setOpen(false)} |
Closes #669.
Problem
On mobile (viewport ≤ 720px), tapping the hamburger opens the navigation sheet, but the dark overlay only covers the top header strip — the hero ("AGENT MEMORY", "THE MEMORY LAYER YOUR CODING AGENT…", etc.) shows through, and the menu items themselves are barely visible because the panel is squeezed into the header strip.
Root cause
Nav.module.cssappliesbackdrop-filter: blur(10px)to the<header>. Per the CSS spec,backdrop-filter(with any non-nonevalue) establishes a containing block for fixed-positioned descendants — same behavior astransformorfilter.The mobile sheet was rendered as a child of that header:
So
position: fixed; inset: 0on.sheetresolved against the header's bounding box — the thin top strip — not the viewport.Fix
Render the sheet through
createPortal(sheet, document.body)so it escapes the nav's containing block and is positioned against the viewport. Also:var(--abyss)for the background so the overlay is fully opaque (no faint hero bleed-through on browsers that composergba(0,0,0,0.94)against bright content).backdrop-filteron the sheet itself — it's redundant once the bg is opaque and was the same property responsible for the original containing-block bug at the nav level.z-index: 95so the nav header (z-index: 100) — and therefore the brand + close (×) button — stays on top of the open sheet.mountedstate) to avoid SSR/hydration mismatch when callingcreatePortal(document.body).Verification
Built and run locally (
npm run build && npm start), opened at 390 × 844 (iPhone 14 Pro) viewport:Before (from issue): hero bleeds through, menu items invisible.
After:
npm run buildpasses (Next 16.2.6 Turbopack, TypeScript clean).Files changed
website/components/MobileNavToggle.tsx— portal sheet todocument.bodybehind a mount guard; no behavior changes to the button, focus handling, or scroll lock.website/components/MobileNavToggle.module.css— opaque bg viavar(--abyss), dropbackdrop-filteron.sheet, retainz-index: 95.Test plan
npm run buildgreenSummary by CodeRabbit
Bug Fixes
Style