Skip to content

fix(ui): portal context menu so dropdown isn't clipped by sibling cards#32

Merged
fullstackjam merged 3 commits into
mainfrom
fix/context-menu-clipped
Jun 28, 2026
Merged

fix(ui): portal context menu so dropdown isn't clipped by sibling cards#32
fullstackjam merged 3 commits into
mainfrom
fix/context-menu-clipped

Conversation

@fullstackjam

Copy link
Copy Markdown
Member

Summary

The config card three-dot menu dropdown was being covered by the card below it (see screenshot in the linked issue/report).

Root cause: the dropdown was position: absolute; z-index: 100 rendered inside each card. Each card creates its own stacking context via transform (.row:hover lift + the .card-wrap fadeUp animation), so the dropdown's z-index is trapped within its own card and can't paint above the next sibling card. Bumping z-index alone can't escape a sibling stacking context.

Fix: render the dropdown as a document.body portal, positioned fixed relative to the trigger (recomputed on resize/scroll). This lifts it out of the per-card stacking context. The shared ContextMenu component is used by both ConfigRow (dashboard) and ConfigCard (preview), so both are fixed in one change. Click-outside now checks both the trigger and the portaled dropdown.

Test plan

  • npm run check — 0 errors
  • npm run lint — 0 errors (no new warnings on the changed file)
  • npm test — 289 passed
  • Manual (Playwright): dashboard with 5 ConfigRow cards, opened the top card's menu — elementFromPoint confirms all four items (Share/Duplicate/Export JSON/Delete) are topmost, nothing covers them; verified visually

The dropdown was position:absolute inside each card, but cards create
their own stacking context via transform (hover lift + fadeUp animation),
trapping the menu's z-index below the next sibling card. Render the
dropdown as a body portal positioned fixed relative to the trigger so it
escapes the per-card stacking context. Fixes ConfigRow and ConfigCard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

fullstackjam and others added 2 commits June 29, 2026 00:06
The dropdown is portaled to document.body and positioned fixed, so .ctx
no longer anchors anything. Removing the obsolete rule the portal change
left behind.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Anchor the dropdown against document.documentElement.clientWidth instead
  of window.innerWidth so a fixed `right` offset lines up with the trigger;
  innerWidth includes the scrollbar and shifted the menu left by its width.
- Lower z-index 1000 -> 150 so it sits above the header but below modal
  overlays (which use 1000) instead of tying them.
- Attach window scroll/resize listeners only while the menu is open, via an
  $effect, so closed menus do no per-scroll work; capture-phase scroll also
  tracks nested scroll containers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fullstackjam fullstackjam merged commit 0d7721d into main Jun 28, 2026
8 checks passed
@fullstackjam fullstackjam deleted the fix/context-menu-clipped branch June 28, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant