Visitar URL original
fix(react): keep basemap controls above the canvas by Davien21 · Pull Request #10997 · visgl/deck.gl · GitHub
Skip to content

fix(react): keep basemap controls above the canvas - #10997

Open
Davien21 wants to merge 7 commits into
visgl:masterfrom
Davien21:fix-map-controls-stacking
Open

Davien21 wants to merge 7 commits into
visgl:masterfrom
Davien21:fix-map-controls-stacking

Conversation

@Davien21

@Davien21 Davien21 commented Nov 2, 2026 •

Copy link
Copy Markdown

Closes #10917

evaluateChildren gives a Map child zIndex: -1, which makes it a stacking context, so its controls' z-index: 2 can't lift them above the canvas. Setting z-index: 1 on the canvas, as the issue suggests, would also push position: absolute children under it, such as the attribution in the terrain-extension example. This renders the map before the canvas instead, so neither needs a z-index.

The main changes are in evaluateChildren (drops the map's zIndex: -1) and positionChildrenUnderViews (returns maps separately, wrapped in view-<id>--map). DeckGL renders maps before the canvas, and Deck inserts a shared device canvas after its placeholder and hides the placeholder. DeckPicker now flips y only on WebGL, since WebGPU picks read from the top-left.

Tested with test/modules/react and deck.spec.ts in headless Chromium, and with a real MapLibre map — the attribution and zoom buttons are on top of the canvas after this change and weren't before.

#10994 changes the same canvas insert in Deck. I can rebase on it if it lands first.


Note

Low Risk
Low risk DOM ordering and styling adjustments for React map integration and shared canvas placement, accompanied by comprehensive unit tests.

Overview
Updates React rendering order so that react-map-gl Map instances are placed before the DeckGL canvas in the DOM instead of relying on zIndex: -1. Removing the negative z-index prevents the map from establishing an isolated stacking context, allowing map controls and attribution (z-index: 2) to naturally render above the deck.gl canvas and receive interactions.

In @deck.gl/core, Deck now hides placeholder canvases and inserts shared device canvases directly adjacent to them in DOM order, restoring the placeholder's previous display state upon finalization. Documentation and the upgrade guide have also been updated to explain the stacking changes.

Reviewed by Cursor Bugbot for commit 775832b. Bugbot is set up for automated code reviews on this repo. Configure here.

@greptile-apps

greptile-apps Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge, with no new actionable issues found.

Summary

Keeps basemap controls above the deck canvas by changing DOM order instead of using a negative z-index.

  • Map backgrounds stay under the canvas while map controls can rise above it.
  • A shared drawing canvas takes the place of its hidden placeholder.

No new actionable issues were found in the changes since the previous review. No previous review threads were supplied.

Reviews (5) · Last reviewed commit: "revert: leave DeckPicker to the WebGPU p..." · Reviewed by Greptile

@chrisgervang chrisgervang left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at 497790435. Two P1s: a shared device canvas now draws 300 px to the right, and the new stacking needs an upgrade-guide entry.

I compared 9.4.1 with and without this PR in headless Chromium, across react-map-gl, MapboxOverlay, GoogleMapsOverlay and pure-JS Deck. Map controls now draw above the layers and take clicks. The overlays look the same on both.

Labels: P1 settle before this is merged, P2 can follow, nit optional.

Comment thread modules/core/src/lib/deck.ts Outdated
const {canvas: placeholder, parent} = this.props;
if (placeholder instanceof HTMLCanvasElement && placeholder.isConnected) {
// Keep the stacking order of the canvas it stands in for
placeholder.after(this.canvas);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

P1: This breaks the website's WebGL2/WebGPU device-tab examples. A shared device canvas now goes in after the 300x150 inline placeholder canvas, and it has no left or top, so it draws 300 px to the right.

  • On the website with 9.4.1 plus this PR, scatterplot-layer and arc-layer draw 300 px off the basemap on both tabs. Hovering a drawn dot or arc picks a different object or nothing.
  • Pure-JS new Deck({device, parent, canvas}) with a static canvas shifts the same way.
  • The new test checks DOM order only, so it passes.
9.4.1 this PR
scatterplot-layer, WebGL2 tab On 9.4.1 the dots cover Manhattan and Brooklyn With this PR the dots start 300 px to the right, off Manhattan

Could we set left: 0; top: 0 on the inserted canvas, or hide the placeholder? #10994 changes this insert too, so one fix could cover both.

Details
  • The same shift shows with react-map-gl MapLibre and Mapbox children and with no basemap, on a device made as in website/src/store/device-store.jsx. It is there on first mount, after a remount, and after client-side navigation.
  • global-grids, which uses an overlay with the shared device, looks the same on both.
  • On 9.4.1 the device canvas covered .deck-events-root, so point-cloud-layer got no hover. With this PR hover reaches deck, but at the shifted spot. An absolute, full-size canvas passed with device now gets hover that lines up.

@Davien21 Davien21 Nov 3, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

All addressed, thanks! 🙏
61c7312 hides the placeholder so the shared canvas no longer shifts, adds the upgrade-guide entry and uses --map. With the canvas in place, WebGPU hover picked the object mirrored top to bottom, because DeckPicker always flipped y for WebGL's framebuffer. e41af4e flips it only on WebGL.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's not change the picker in this PR. We prefer small focused PRs for core changes, and we had already started to fix the picker in #11020

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Makes sense, reverted in 775832b so this PR only covers canvas mounting. I'll leave the picker to #11019.


const MAP_STYLE = {position: 'absolute', zIndex: -1};
// No z-index: it would make the map a stacking context and trap its controls under the canvas
const MAP_STYLE = {position: 'absolute'};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

P1: Map controls now draw above any <DeckGL> child without z-index: 2 or higher. Apps will see this in a 9.4 patch, so it needs an upgrade-guide entry.

master head
Overlay child On master the red child covers the attribution On the head the attribution draws over the red child

Could we add this under "Upgrading to v9.4"?

### React: basemap controls draw above layers (v9.4.2)

This applies if you render a react-map-gl `<Map>` inside `<DeckGL>` along with your own HTML elements, such as a legend or panel.

Map controls and attribution now draw above deck.gl layers and take clicks. They can also cover your elements. To keep an element on top, give it `position: absolute` and `z-index: 2` or higher.
Details
  • With MapLibre and Mapbox, a red div child with no z-index or zIndex: 1 ends up under the attribution. With zIndex: 2 it stays on top.
  • Deck widgets come later in the DOM at z-index 2, so they stay above map controls.
  • The DeckGL wrapper keeps zIndex: 0, so map controls do not rise above page elements outside it.

);

if (viewMaps.length) {
maps.push(wrapChildren(`view-${viewId}-map`, viewMaps));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: Views main and main-map would both produce the DOM id view-main-map. The PR body says view-<id>--map. Could the code and body agree on one suffix?

The shared device canvas went in after the inline placeholder, so it drew
300 px to the right. Also adds the upgrade-guide entry and uses --map for
the map wrapper id so it can't collide with a view named <id>-map.
@Davien21
Davien21 force-pushed the fix-map-controls-stacking branch from 4977904 to 0a6e3b3 Compare November 3, 2026 06:40

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread modules/core/src/lib/deck.ts Resolved
Davien21 and others added 2 commits November 3, 2026 11:46
DeckPicker always asked cssToDevicePixels for bottom-left coordinates,
which suits WebGL. WebGPU scissor rects and texture reads start at the
top-left, so every WebGPU pick read the row mirrored top to bottom. Invert
y only when the device isn't WebGPU, in both point and rect picks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nalize

Deck hides props.canvas while a shared device canvas draws in its place,
but left it hidden after finalize. Remember its inline display and put it
back when Deck is finalized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 34e9350. Configure here.

Comment thread modules/core/src/lib/deck.ts Resolved
Davien21 and others added 2 commits November 3, 2026 12:46
After finalize restored props.canvas, a new Deck reusing the device found
the shared canvas still in the DOM, skipped the hide, and drew 300 px to
the right again. Hide the stand-in whenever a shared canvas draws in its
place, and take the shared canvas out on finalize so the restored canvas
can't offset it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This reverts commit e41af4e. The mirrored WebGPU picking is tracked in
visgl#11020 and fixed separately, so this PR stays on canvas mounting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

[Bug] Basemap controls and attribution are drawn under the deck canvas in <DeckGL><Map /></DeckGL>

2 participants