Problem
The built-in Browser combines React UI with a native Electron WebContentsView. The native page does not participate in the renderer DOM stacking context, so it can paint above canvas UI even when that UI has a higher CSS z-index.
Two cases were observed on macOS:
- When the Browser approaches the top of the application, the native page surface and its scrollbar can extend into UI outside the expected page viewport.
- When the Browser overlaps the bottom-right keyboard shortcut overlay, the page covers the overlay background and part of its text despite
.shortcut-hints using z-index: 300.
The same problem can affect other DOM-backed canvas widgets that should appear above the Browser.
Root cause
BrowserService.syncViews mounts the active page as a native WebContentsView under BrowserWindow.contentView.
Other canvas widgets and overlays are rendered inside the React renderer. CSS z-index cannot change stacking order across the native/DOM boundary.
The existing clipping logic constrains the Browser to a rectangular viewport and application bounds, but it does not account for higher DOM overlays or overlapping widgets.
Relevant code:
src/main/services/BrowserService.ts: syncViews, mountClipTab, clipView
src/main/services/browser/BrowserCanvasGestureController.ts: surfaceDecision, activateFreezeFrame, refreshFrame
src/renderer/src/features/browser/BrowserCard.tsx: reportViewport, freezeFrameVisible, nativeViewVisible
src/renderer/src/features/workspace/WorkspaceCanvas.tsx: shortcut-hints
src/renderer/src/styles/app.css: .shortcut-hints, .canvas-controls
Expected behavior
- Application chrome and persistent overlays remain visible above the Browser.
- Canvas widget ordering remains visually consistent when widgets overlap.
- The live Browser remains interactive when unobscured.
- Restoring the live native view does not cause a geometry jump or show a stale frame.
Proposed direction
Introduce an occlusion-aware composition policy instead of relying on CSS z-index:
- Treat fixed application chrome as explicit safe areas and keep the native viewport outside them.
- Render suitable persistent overlays in a trusted native overlay, or relocate them when they intersect the Browser.
- For arbitrary overlaps with higher DOM widgets, extend the existing freeze-frame mechanism:
- capture the current page;
- hide the native
WebContentsView;
- display the snapshot inside
BrowserCard;
- restore the live view when the Browser is selected or brought to the front.
- Invalidate captured frames after navigation, tab changes, resize, canvas zoom, and live/snapshot transitions.
Rectangular insets can solve fixed safe areas, but they cannot represent arbitrary overlap between multiple widgets and should not be the only mechanism.
Reproduction
- Start CanvasTTY on macOS and open the built-in Browser.
- Move or resize the Browser near the top application UI.
- Inspect the native page and scrollbar at the upper boundary.
- Move or resize the Browser so it intersects the bottom-right keyboard shortcut overlay.
- Observe that the native page paints above the overlay despite its higher DOM
z-index.
- Repeat with another DOM widget positioned visually above the Browser.
Environment
- macOS
- Electron 43.2.0
- Observed on
feat/canvas-trackpad-navigation
Acceptance criteria
Problem
The built-in Browser combines React UI with a native Electron
WebContentsView. The native page does not participate in the renderer DOM stacking context, so it can paint above canvas UI even when that UI has a higher CSSz-index.Two cases were observed on macOS:
.shortcut-hintsusingz-index: 300.The same problem can affect other DOM-backed canvas widgets that should appear above the Browser.
Root cause
BrowserService.syncViewsmounts the active page as a nativeWebContentsViewunderBrowserWindow.contentView.Other canvas widgets and overlays are rendered inside the React renderer. CSS
z-indexcannot change stacking order across the native/DOM boundary.The existing clipping logic constrains the Browser to a rectangular viewport and application bounds, but it does not account for higher DOM overlays or overlapping widgets.
Relevant code:
src/main/services/BrowserService.ts:syncViews,mountClipTab,clipViewsrc/main/services/browser/BrowserCanvasGestureController.ts:surfaceDecision,activateFreezeFrame,refreshFramesrc/renderer/src/features/browser/BrowserCard.tsx:reportViewport,freezeFrameVisible,nativeViewVisiblesrc/renderer/src/features/workspace/WorkspaceCanvas.tsx:shortcut-hintssrc/renderer/src/styles/app.css:.shortcut-hints,.canvas-controlsExpected behavior
Proposed direction
Introduce an occlusion-aware composition policy instead of relying on CSS
z-index:WebContentsView;BrowserCard;Rectangular insets can solve fixed safe areas, but they cannot represent arbitrary overlap between multiple widgets and should not be the only mechanism.
Reproduction
z-index.Environment
feat/canvas-trackpad-navigationAcceptance criteria
shortcut-hintsand other persistent overlays remain visible above the Browser.z-index.WebContentsViewwithout a geometry jump.