Summary
When pixelSizeSource: 'css-dpr' is set on CanvasContextProps, the CanvasObserver still observes the canvas with {box: 'device-pixel-content-box'}. This causes major misalignment when used with basemap overlays (Mapbox/MapLibre/Google Maps) during browser zoom changes.
Root Cause
Two issues in the current css-dpr implementation (shipped in 9.3.4 via #2588):
1. Wrong observer box (critical — causes massive shift)
CanvasObserver.start() always uses device-pixel-content-box:
this._resizeObserver.observe(this.props.canvas, {box: 'device-pixel-content-box'});
When the browser zoom changes (Cmd+/-), this observer fires with physical pixel counts that may be stale or racey relative to when Mapbox recalculates its canvas size. Since css-dpr mode ignores the devicePixelContentBoxSize anyway and recalculates from contentBoxSize * dpr, the observer should just watch content-box — which only fires when CSS dimensions actually change, leaving DPR-driven resize to the existing matchMedia + _refreshDevicePixelRatio path.
2. Wrong rounding (minor — causes 1px mismatch)
_getDevicePixelSizeFromResizeEntry uses Math.round:
devicePixelWidth: Math.round(contentBoxSize.inlineSize * devicePixelRatio),
MapLibre uses Math.floor(pixelRatio * width). Mapbox uses pixelRatio * Math.ceil(width) which for integer CSS sizes (the common case) is equivalent to Math.floor after implicit truncation on canvas.width assignment. Using Math.round can produce a value 1px larger than the basemap at certain DPR values.
Proposed Fix
When pixelSizeSource === 'css-dpr':
- Pass the mode through to
CanvasObserver so it observes with {box: 'content-box'}
- Change
Math.round to Math.floor in _getDevicePixelSizeFromResizeEntry
Verification
Tested with deck.gl's MapboxOverlay in overlaid mode:
- Without fix: massive canvas offset at 50%/25% browser zoom, 1px mismatch at 67%/90%
- With fix: pixel-perfect alignment at all tested zoom levels (25%, 33%, 50%, 67%, 75%, 80%, 90%, 100%, 110%, 125%, 150%, 175%, 200%)

50%

25%

100%

50%

[
25%
Context
Summary
When
pixelSizeSource: 'css-dpr'is set onCanvasContextProps, theCanvasObserverstill observes the canvas with{box: 'device-pixel-content-box'}. This causes major misalignment when used with basemap overlays (Mapbox/MapLibre/Google Maps) during browser zoom changes.Root Cause
Two issues in the current
css-dprimplementation (shipped in 9.3.4 via #2588):1. Wrong observer box (critical — causes massive shift)
CanvasObserver.start()always usesdevice-pixel-content-box:When the browser zoom changes (Cmd+/-), this observer fires with physical pixel counts that may be stale or racey relative to when Mapbox recalculates its canvas size. Since
css-dprmode ignores thedevicePixelContentBoxSizeanyway and recalculates fromcontentBoxSize * dpr, the observer should just watchcontent-box— which only fires when CSS dimensions actually change, leaving DPR-driven resize to the existingmatchMedia+_refreshDevicePixelRatiopath.2. Wrong rounding (minor — causes 1px mismatch)
_getDevicePixelSizeFromResizeEntryusesMath.round:MapLibre uses
Math.floor(pixelRatio * width). Mapbox usespixelRatio * Math.ceil(width)which for integer CSS sizes (the common case) is equivalent toMath.floorafter implicit truncation on canvas.width assignment. UsingMath.roundcan produce a value 1px larger than the basemap at certain DPR values.Proposed Fix
When
pixelSizeSource === 'css-dpr':CanvasObserverso it observes with{box: 'content-box'}Math.roundtoMath.floorin_getDevicePixelSizeFromResizeEntryVerification
Tested with deck.gl's
MapboxOverlayin overlaid mode:Context
pixelSizeSource: fix(mapbox,google-maps): use css-dpr pixel sizing in overlaid mode to prevent basemap misalignment deck.gl#10370pixelSizeSourcePR: feat(core) Support legacy sizing in CanvasContext #2588content-boxfix: [Bug] Misalignment/drift in MapboxOverlay at fractional zoom levels deck.gl#10173 (comment)