Disclaimer: I used Kiro CLI to troubleshoot and prepare this issue report.
Description
When a scattergl trace has marker.color set to an array of CSS color strings (discrete colors, no colorscale), hovering a point logs a warning and the hover label color defaults to black:
WARN: Invalid color specifier: "rgb(23, 103, 194),rgb(23, 103, 194),…,#FF4136,…". Defaulting to "#000"
The entire color array is stringified and passed to the color parser as a single value. The markers themselves render with correct per-point colors; only the hover/legend representative color is wrong, plus the console warning.
This is closely related to #2953 (closed) — same code path — but in v4 it now also emits an invalid-color warning rather than silently falling back.
Root cause
src/traces/scattergl/hover.js → calcHover() correctly computes the per-point marker color as di.mc = marker.color[id], but then calls getTraceColor(trace, di).
src/traces/scatter/get_trace_color.js reads:
var mc = di.mcc || (trace.marker || {}).color;
di.mcc is only populated for numeric color arrays mapped through a colorscale. For a discrete string array, mcc is undefined, so mc falls back to the whole trace.marker.color array, and Color.opacity(mc) receives the entire array joined as one string.
SVG scatter is unaffected because its calcdata pipeline sets mcc per point; scattergl's calcHover sets di.mc (already indexed) but never di.mcc.
Steps to reproduce
Plotly.newPlot('graph', [{
type: 'scattergl',
mode: 'markers',
x: [1, 2, 3],
y: [1, 2, 3],
marker: { color: ['rgb(23, 103, 194)', '#FF4136', 'rgb(23, 103, 194)'], size: 20 }
}]);
// Hover any point → console warns "Invalid color specifier: …"
Notes
Version
plotly.js v4.0.0
Proposed fix (PR diff)
The minimal, safe change is one line in src/traces/scatter/get_trace_color.js:
--- a/src/traces/scatter/get_trace_color.js
+++ b/src/traces/scatter/get_trace_color.js
@@ module.exports = function getTraceColor(trace, di) {
} else {
- var mc = di.mcc || (trace.marker || {}).color;
+ // di.mcc is the colorscale-mapped per-point color (numeric color
+ // arrays). di.mc is the per-point color set by the scattergl hover
+ // path for discrete string color arrays. Fall back to the trace-level
+ // marker.color only when neither per-point value is available, so we
+ // never pass an entire color array to Color.opacity().
+ var mc = di.mcc || di.mc || (trace.marker || {}).color;
var mlc = di.mlcc || ((trace.marker || {}).line || {}).color;
Why this is correct and low-risk:
di.mc is already the per-point value the hover code computed (marker.color[id]), so for the string-array case it becomes the right single color instead of the whole array.
- For SVG scatter,
di.mcc is set and takes precedence, so behavior is unchanged.
- For the numeric+colorscale case,
di.mcc still wins — unchanged.
- The only behavioral change is the previously-broken gl string-array path, which now returns the correct point color.
Optionally, add a mock/test under test/jasmine/tests/scattergl_test.js asserting that hovering a scattergl trace with a string-array marker.color produces no Lib.warn and the returned hover color equals the point's color.
Disclaimer: I used Kiro CLI to troubleshoot and prepare this issue report.
Description
When a
scattergltrace hasmarker.colorset to an array of CSS color strings (discrete colors, no colorscale), hovering a point logs a warning and the hover label color defaults to black:The entire color array is stringified and passed to the color parser as a single value. The markers themselves render with correct per-point colors; only the hover/legend representative color is wrong, plus the console warning.
This is closely related to #2953 (closed) — same code path — but in v4 it now also emits an invalid-color warning rather than silently falling back.
Root cause
src/traces/scattergl/hover.js→calcHover()correctly computes the per-point marker color asdi.mc = marker.color[id], but then callsgetTraceColor(trace, di).src/traces/scatter/get_trace_color.jsreads:di.mccis only populated for numeric color arrays mapped through acolorscale. For a discrete string array,mccis undefined, somcfalls back to the wholetrace.marker.colorarray, andColor.opacity(mc)receives the entire array joined as one string.SVG
scatteris unaffected because its calcdata pipeline setsmccper point;scattergl'scalcHoversetsdi.mc(already indexed) but neverdi.mcc.Steps to reproduce
Notes
Version
plotly.js v4.0.0
Proposed fix (PR diff)
The minimal, safe change is one line in
src/traces/scatter/get_trace_color.js:Why this is correct and low-risk:
di.mcis already the per-point value the hover code computed (marker.color[id]), so for the string-array case it becomes the right single color instead of the whole array.di.mccis set and takes precedence, so behavior is unchanged.di.mccstill wins — unchanged.Optionally, add a
mock/test under test/jasmine/tests/scattergl_test.jsasserting that hovering ascattergltrace with a string-arraymarker.colorproduces noLib.warnand the returned hover color equals the point's color.