Skip to content

Hide preview overlay while popups are open#110

Closed
BunsDev wants to merge 1 commit intomainfrom
okcode/dropdown-z-index
Closed

Hide preview overlay while popups are open#110
BunsDev wants to merge 1 commit intomainfrom
okcode/dropdown-z-index

Conversation

@BunsDev
Copy link
Copy Markdown
Contributor

@BunsDev BunsDev commented Mar 31, 2026

Summary

  • Hide the native preview BrowserView overlay whenever popup, dropdown, or combobox positioners are present in the DOM.
  • Add a DOM MutationObserver so the preview bounds are invalidated immediately when popups mount or unmount.
  • Prevent the overlay from rendering on top of menus and popovers, which do not respect CSS z-index.

Testing

  • Not run (PR content only).

- Detect portal dropdown and popover positioners in the web UI
- Suspend the native BrowserView overlay so menus render above it
@github-actions github-actions bot added size:S vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Mar 31, 2026
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 31, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
okcode-marketing Ready Ready Preview Mar 31, 2026 5:03pm

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR prevents the native desktop preview (BrowserView) overlay from drawing above web UI popups by hiding the overlay whenever popup/dropdown/combobox “positioner” elements are present in the DOM, and attempts to react immediately to popup mount/unmount events.

Changes:

  • Add a shared selector for Base UI popup “positioner” elements portaled to document.body.
  • Hide the native preview overlay (visible: false) when any matching positioner is present.
  • Add a MutationObserver to invalidate bounds when DOM children are added/removed.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +133 to +141
// Hide the native BrowserView when any popup/dropdown is open so it
// doesn't render on top of menus (native overlays ignore CSS z-index).
const hasOpenPopup = document.querySelector(POPUP_POSITIONER_SELECTOR) !== null;
const visible =
tabsState.tabs.length > 0 &&
document.visibilityState === "visible" &&
rect.width > 0 &&
rect.height > 0;
rect.height > 0 &&
!hasOpenPopup;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computeBounds runs on every animation frame (the RAF loop in syncBounds). Adding document.querySelector(POPUP_POSITIONER_SELECTOR) here means we traverse the DOM ~60 times/sec while the preview bridge is active, even when the preview isn't eligible to be shown. Consider avoiding the per-frame query by (a) short-circuiting on the cheap checks first (tabs length, document visibility, rect size) and only querying when those pass, and/or (b) caching hasOpenPopup in a ref that’s updated by the MutationObserver / popup lifecycle so computeBounds is O(1).

Copilot uses AI. Check for mistakes.

// Watch for popup positioners being added/removed from the DOM so we
// can immediately hide/show the native BrowserView overlay.
const popupObserver = new MutationObserver(invalidateBounds);
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The MutationObserver invalidates bounds on any direct child add/remove under document.body, even when the change is unrelated to popup positioners (e.g., toast portals, other overlays). Because invalidateBounds forces lastBoundsKey to "", this can trigger extra previewBridge.setBounds(...) calls (IPC) with unchanged geometry. Consider filtering mutation records to only invalidate when nodes matching (or containing) POPUP_POSITIONER_SELECTOR are added/removed, or remove the observer entirely if the always-on RAF loop is sufficient for responsiveness.

Suggested change
const popupObserver = new MutationObserver(invalidateBounds);
const popupObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type !== "childList") continue;
for (const node of Array.from(mutation.addedNodes)) {
if (node instanceof Element) {
if (
(typeof (node as Element).matches === "function" &&
(node as Element).matches(POPUP_POSITIONER_SELECTOR)) ||
(typeof (node as Element).querySelector === "function" &&
(node as Element).querySelector(POPUP_POSITIONER_SELECTOR))
) {
invalidateBounds();
return;
}
}
}
for (const node of Array.from(mutation.removedNodes)) {
if (node instanceof Element) {
if (
(typeof (node as Element).matches === "function" &&
(node as Element).matches(POPUP_POSITIONER_SELECTOR)) ||
(typeof (node as Element).querySelector === "function" &&
(node as Element).querySelector(POPUP_POSITIONER_SELECTOR))
) {
invalidateBounds();
return;
}
}
}
}
});

Copilot uses AI. Check for mistakes.
Comment on lines +133 to +141
// Hide the native BrowserView when any popup/dropdown is open so it
// doesn't render on top of menus (native overlays ignore CSS z-index).
const hasOpenPopup = document.querySelector(POPUP_POSITIONER_SELECTOR) !== null;
const visible =
tabsState.tabs.length > 0 &&
document.visibilityState === "visible" &&
rect.width > 0 &&
rect.height > 0;
rect.height > 0 &&
!hasOpenPopup;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change introduces new behavior (hiding the native BrowserView overlay when popup positioners exist in the DOM), but there are currently no tests asserting the bounds/visibility behavior (existing test only checks the module exports). If feasible, add a unit test that stubs previewBridge.setBounds and verifies visible flips to false when an element matching POPUP_POSITIONER_SELECTOR is present and back to true when it’s removed.

Copilot uses AI. Check for mistakes.
Copilot AI added a commit that referenced this pull request Mar 31, 2026
BunsDev pushed a commit that referenced this pull request Mar 31, 2026
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
@BunsDev BunsDev closed this Mar 31, 2026
@BunsDev BunsDev deleted the okcode/dropdown-z-index branch March 31, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants