Skip to content

[BUG] Colorbar thickness becomes too wide (~16k px) when marker.colorbar.thicknessmode='pixels' is applied via Plotly.restyle #8012

Description

@LeonxLJX

Description

For a splom trace whose marker.colorbar exists but has no explicit thickness
(plotly.js defaults it), applying marker.colorbar.thicknessmode='pixels' afterwards with
Plotly.restyle collapses the whole splom layout:

  • plotly.js silently writes thickness: 16290 back into the user-visible trace
    (gd.data[1].marker.colorbar.thickness),
  • the colorbar then occupies ~16k px and the splom panels shrink from ~125 px to ~15 px per axis.

Setting the exact same value at newPlot time (Path A in the repro) renders fine.

Reproduced on every version I could test: 2.35.2, 3.5.0, 3.5.1, and current 3.8.2.

Steps to reproduce

Save the following as index.html and open it in a browser
(uses https://cdn.plot.ly/plotly-3.5.0.min.js; the bundled copy from any release works too).
The page renders both paths, then prints the colorbar object and the panel width under each plot.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>plotly.js splom + marker.colorbar restyle bug</title>
<!-- any plotly.js >= 2.35 works: use your own bundle or the CDN below -->
<script src="https://cdn.plot.ly/plotly-3.5.0.min.js"></script>
<style>
  body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; margin: 16px; }
  .row { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
  h3 { margin: 10px 0 4px; font-size: 15px; }
  pre { background: #f6f8fa; border-radius: 6px; padding: 10px; font-size: 12px;
        white-space: pre-wrap; max-width: 780px; }
  code { background: #f6f8fa; padding: 1px 4px; border-radius: 4px; }
</style>
</head>
<body>
<h1>splom layout collapses when <code>marker.colorbar.thicknessmode='pixels'</code>
is applied with <code>Plotly.restyle</code></h1>
<p>Same data + layout, same final <code>marker.colorbar</code> value. Difference is only <b>when</b>
it is applied:</p>
<ul>
  <li><b>Path A (normal):</b> <code>marker.colorbar = {thicknessmode: 'pixels'}</code> is in the
      figure at <code>newPlot</code> time.</li>
  <li><b>Path B (collapsed):</b> figure starts with <code>marker.colorbar = {}</code> (no explicit
      <code>thickness</code>), then <code>Plotly.restyle(gd, {'marker.colorbar.thicknessmode':'pixels'}, [1])</code>.</li>
</ul>
<p>After Path B, plotly.js writes <b>thickness: 16290</b> back into the trace's colorbar and the
splom panels collapse from ~125px to ~15px per axis.</p>

<div class="row">
  <div>
    <h3>Path A — set at newPlot time (normal)</h3>
    <div id="A" class="gd" style="width:750px;height:900px"></div>
    <pre id="mA">…</pre>
  </div>
  <div>
    <h3>Path B — set via Plotly.restyle (collapsed)</h3>
    <div id="B" class="gd" style="width:750px;height:900px"></div>
    <pre id="mB">…</pre>
  </div>
</div>

<script>
var DATA = [{"dimensions": [{"axis": {"matches": true}, "label": "sepal_length", "values": {"dtype": "f8", "bdata": "ZmZmZmZmFECamZmZmZkTQM3MzMzMzBJAZmZmZmZmEkAAAAAAAAAUQJqZmZmZmRVAZmZmZmZmEkAAAAAAAAAUQJqZmZmZmRFAmpmZmZmZE0CamZmZmZkVQDMzMzMzMxNAMzMzMzMzE0AzMzMzMzMRQDMzMzMzMxdAzczMzMzMFkCamZmZmZkVQGZmZmZmZhRAzczMzMzMFkBmZmZmZmYUQJqZmZmZmRVAZmZmZmZmFEBmZmZmZmYSQGZmZmZmZhRAMzMzMzMzE0AAAAAAAAAUQAAAAAAAABRAzczMzMzMFEDNzMzMzMwUQM3MzMzMzBJAMzMzMzMzE0CamZmZmZkVQM3MzMzMzBRAAAAAAAAAFkCamZmZmZkTQAAAAAAAABRAAAAAAAAAFkCamZmZmZkTQJqZmZmZmRFAZmZmZmZmFEAAAAAAAAAUQAAAAAAAABJAmpmZmZmZEUAAAAAAAAAUQGZmZmZmZhRAMzMzMzMzE0BmZmZmZmYUQGZmZmZmZhJAMzMzMzMzFUAAAAAAAAAUQA=="}}, {"axis": {"matches": true}, "label": "sepal_width", "values": {"dtype": "f8", "bdata": "AAAAAAAADEAAAAAAAAAIQJqZmZmZmQlAzczMzMzMCEDNzMzMzMwMQDMzMzMzMw9AMzMzMzMzC0AzMzMzMzMLQDMzMzMzMwdAzczMzMzMCECamZmZmZkNQDMzMzMzMwtAAAAAAAAACEAAAAAAAAAIQAAAAAAAABBAmpmZmZmZEUAzMzMzMzMPQAAAAAAAAAxAZmZmZmZmDkBmZmZmZmYOQDMzMzMzMwtAmpmZmZmZDUDNzMzMzMwMQGZmZmZmZgpAMzMzMzMzC0AAAAAAAAAIQDMzMzMzMwtAAAAAAAAADEAzMzMzMzMLQJqZmZmZmQlAzczMzMzMCEAzMzMzMzMLQGZmZmZmZhBAzczMzMzMEEDNzMzMzMwIQJqZmZmZmQlAAAAAAAAADEDNzMzMzMwIQAAAAAAAAAhAMzMzMzMzC0AAAAAAAAAMQGZmZmZmZgJAmpmZmZmZCUAAAAAAAAAMQGZmZmZmZg5AAAAAAAAACEBmZmZmZmYOQJqZmZmZmQlAmpmZmZmZDUBmZmZmZmYKQA=="}}, {"axis": {"matches": true}, "label": "petal_length", "values": {"dtype": "f8", "bdata": "ZmZmZmZm9j9mZmZmZmb2P83MzMzMzPQ/AAAAAAAA+D9mZmZmZmb2PzMzMzMzM/s/ZmZmZmZm9j8AAAAAAAD4P2ZmZmZmZvY/AAAAAAAA+D8AAAAAAAD4P5qZmZmZmfk/ZmZmZmZm9j+amZmZmZnxPzMzMzMzM/M/AAAAAAAA+D/NzMzMzMz0P2ZmZmZmZvY/MzMzMzMz+z8AAAAAAAD4PzMzMzMzM/s/AAAAAAAA+D8AAAAAAADwPzMzMzMzM/s/ZmZmZmZm/j+amZmZmZn5P5qZmZmZmfk/AAAAAAAA+D9mZmZmZmb2P5qZmZmZmfk/mpmZmZmZ+T8AAAAAAAD4PwAAAAAAAPg/ZmZmZmZm9j8AAAAAAAD4PzMzMzMzM/M/zczMzMzM9D8AAAAAAAD4P83MzMzMzPQ/AAAAAAAA+D/NzMzMzMz0P83MzMzMzPQ/zczMzMzM9D+amZmZmZn5P2ZmZmZmZv4/ZmZmZmZm9j+amZmZmZn5P2ZmZmZmZvY/AAAAAAAA+D9mZmZmZmb2Pw=="}}, {"axis": {"matches": true}, "label": "petal_width", "values": {"dtype": "f8", "bdata": "mpmZmZmZyT+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnJP5qZmZmZmdk/MzMzMzMz0z+amZmZmZnJP5qZmZmZmck/mpmZmZmZuT+amZmZmZnJP5qZmZmZmck/mpmZmZmZuT+amZmZmZm5P5qZmZmZmck/mpmZmZmZ2T+amZmZmZnZPzMzMzMzM9M/MzMzMzMz0z8zMzMzMzPTP5qZmZmZmck/mpmZmZmZ2T+amZmZmZnJPwAAAAAAAOA/mpmZmZmZyT+amZmZmZnJP5qZmZmZmdk/mpmZmZmZyT+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnZP5qZmZmZmbk/mpmZmZmZyT+amZmZmZm5P5qZmZmZmck/mpmZmZmZyT+amZmZmZm5P5qZmZmZmck/mpmZmZmZyT8zMzMzMzPTPzMzMzMzM9M/mpmZmZmZyT8zMzMzMzPjP5qZmZmZmdk/MzMzMzMz0z+amZmZmZnJP5qZmZmZmck/mpmZmZmZyT+amZmZmZnJPw=="}}], "hovertemplate": "species=setosa<br>%{xaxis.title.text}=%{x}<br>%{yaxis.title.text}=%{y}<extra></extra>", "legendgroup": "setosa", "marker": {"color": "#636efa", "symbol": "circle"}, "name": "setosa", "showlegend": true, "type": "splom"}, {"dimensions": [{"axis": {"matches": true}, "label": "sepal_length", "values": {"dtype": "f8", "bdata": "AAAAAAAAHECamZmZmZkZQJqZmZmZmRtAAAAAAAAAFkAAAAAAAAAaQM3MzMzMzBZAMzMzMzMzGUCamZmZmZkTQGZmZmZmZhpAzczMzMzMFEAAAAAAAAAUQJqZmZmZmRdAAAAAAAAAGEBmZmZmZmYYQGZmZmZmZhZAzczMzMzMGkBmZmZmZmYWQDMzMzMzMxdAzczMzMzMGEBmZmZmZmYWQJqZmZmZmRdAZmZmZmZmGEAzMzMzMzMZQGZmZmZmZhhAmpmZmZmZGUBmZmZmZmYaQDMzMzMzMxtAzczMzMzMGkAAAAAAAAAYQM3MzMzMzBZAAAAAAAAAFkAAAAAAAAAWQDMzMzMzMxdAAAAAAAAAGECamZmZmZkVQAAAAAAAABhAzczMzMzMGkAzMzMzMzMZQGZmZmZmZhZAAAAAAAAAFkAAAAAAAAAWQGZmZmZmZhhAMzMzMzMzF0AAAAAAAAAUQGZmZmZmZhZAzczMzMzMFkDNzMzMzMwWQM3MzMzMzBhAZmZmZmZmFEDNzMzMzMwWQA=="}}, {"axis": {"matches": true}, "label": "sepal_width", "values": {"dtype": "f8", "bdata": "mpmZmZmZCUCamZmZmZkJQM3MzMzMzAhAZmZmZmZmAkBmZmZmZmYGQGZmZmZmZgZAZmZmZmZmCkAzMzMzMzMDQDMzMzMzMwdAmpmZmZmZBUAAAAAAAAAAQAAAAAAAAAhAmpmZmZmZAUAzMzMzMzMHQDMzMzMzMwdAzczMzMzMCEAAAAAAAAAIQJqZmZmZmQVAmpmZmZmZAUAAAAAAAAAEQJqZmZmZmQlAZmZmZmZmBkAAAAAAAAAEQGZmZmZmZgZAMzMzMzMzB0AAAAAAAAAIQGZmZmZmZgZAAAAAAAAACEAzMzMzMzMHQM3MzMzMzARAMzMzMzMzA0AzMzMzMzMDQJqZmZmZmQVAmpmZmZmZBUAAAAAAAAAIQDMzMzMzMwtAzczMzMzMCEBmZmZmZmYCQAAAAAAAAAhAAAAAAAAABEDNzMzMzMwEQAAAAAAAAAhAzczMzMzMBEBmZmZmZmYCQJqZmZmZmQVAAAAAAAAACEAzMzMzMzMHQDMzMzMzMwdAAAAAAAAABEBmZmZmZmYGQA=="}}, {"axis": {"matches": true}, "label": "petal_length", "values": {"dtype": "f8", "bdata": "zczMzMzMEkAAAAAAAAASQJqZmZmZmRNAAAAAAAAAEEBmZmZmZmYSQAAAAAAAABJAzczMzMzMEkBmZmZmZmYKQGZmZmZmZhJAMzMzMzMzD0AAAAAAAAAMQM3MzMzMzBBAAAAAAAAAEEDNzMzMzMwSQM3MzMzMzAxAmpmZmZmZEUAAAAAAAAASQGZmZmZmZhBAAAAAAAAAEkAzMzMzMzMPQDMzMzMzMxNAAAAAAAAAEECamZmZmZkTQM3MzMzMzBJAMzMzMzMzEUCamZmZmZkRQDMzMzMzMxNAAAAAAAAAFEAAAAAAAAASQAAAAAAAAAxAZmZmZmZmDkCamZmZmZkNQDMzMzMzMw9AZmZmZmZmFEAAAAAAAAASQAAAAAAAABJAzczMzMzMEkCamZmZmZkRQGZmZmZmZhBAAAAAAAAAEECamZmZmZkRQGZmZmZmZhJAAAAAAAAAEEBmZmZmZmYKQM3MzMzMzBBAzczMzMzMEEDNzMzMzMwQQDMzMzMzMxFAAAAAAAAACEBmZmZmZmYQQA=="}}, {"axis": {"matches": true}, "label": "petal_width", "values": {"dtype": "f8", "bdata": "ZmZmZmZm9j8AAAAAAAD4PwAAAAAAAPg/zczMzMzM9D8AAAAAAAD4P83MzMzMzPQ/mpmZmZmZ+T8AAAAAAADwP83MzMzMzPQ/ZmZmZmZm9j8AAAAAAADwPwAAAAAAAPg/AAAAAAAA8D9mZmZmZmb2P83MzMzMzPQ/ZmZmZmZm9j8AAAAAAAD4PwAAAAAAAPA/AAAAAAAA+D+amZmZmZnxP83MzMzMzPw/zczMzMzM9D8AAAAAAAD4PzMzMzMzM/M/zczMzMzM9D9mZmZmZmb2P2ZmZmZmZvY/MzMzMzMz+z8AAAAAAAD4PwAAAAAAAPA/mpmZmZmZ8T8AAAAAAADwPzMzMzMzM/M/mpmZmZmZ+T8AAAAAAAD4P5qZmZmZmfk/AAAAAAAA+D/NzMzMzMz0P83MzMzMzPQ/zczMzMzM9D8zMzMzMzPzP2ZmZmZmZvY/MzMzMzMz8z8AAAAAAADwP83MzMzMzPQ/MzMzMzMz8z/NzMzMzMz0P83MzMzMzPQ/mpmZmZmZ8T/NzMzMzMz0Pw=="}}], "hovertemplate": "species=versicolor<br>%{xaxis.title.text}=%{x}<br>%{yaxis.title.text}=%{y}<extra></extra>", "legendgroup": "versicolor", "marker": {"color": "#EF553B", "symbol": "circle"}, "name": "versicolor", "showlegend": true, "type": "splom"}, {"dimensions": [{"axis": {"matches": true}, "label": "sepal_length", "values": {"dtype": "f8", "bdata": "MzMzMzMzGUAzMzMzMzMXQGZmZmZmZhxAMzMzMzMzGUAAAAAAAAAaQGZmZmZmZh5AmpmZmZmZE0AzMzMzMzMdQM3MzMzMzBpAzczMzMzMHEAAAAAAAAAaQJqZmZmZmRlAMzMzMzMzG0DNzMzMzMwWQDMzMzMzMxdAmpmZmZmZGUAAAAAAAAAaQM3MzMzMzB5AzczMzMzMHkAAAAAAAAAYQJqZmZmZmRtAZmZmZmZmFkDNzMzMzMweQDMzMzMzMxlAzczMzMzMGkDNzMzMzMwcQM3MzMzMzBhAZmZmZmZmGECamZmZmZkZQM3MzMzMzBxAmpmZmZmZHUCamZmZmZkfQJqZmZmZmRlAMzMzMzMzGUBmZmZmZmYYQM3MzMzMzB5AMzMzMzMzGUCamZmZmZkZQAAAAAAAABhAmpmZmZmZG0DNzMzMzMwaQJqZmZmZmRtAMzMzMzMzF0AzMzMzMzMbQM3MzMzMzBpAzczMzMzMGkAzMzMzMzMZQAAAAAAAABpAzczMzMzMGECamZmZmZkXQA=="}}, {"axis": {"matches": true}, "label": "sepal_width", "values": {"dtype": "f8", "bdata": "ZmZmZmZmCkCamZmZmZkFQAAAAAAAAAhAMzMzMzMzB0AAAAAAAAAIQAAAAAAAAAhAAAAAAAAABEAzMzMzMzMHQAAAAAAAAARAzczMzMzMDECamZmZmZkJQJqZmZmZmQVAAAAAAAAACEAAAAAAAAAEQGZmZmZmZgZAmpmZmZmZCUAAAAAAAAAIQGZmZmZmZg5AzczMzMzMBECamZmZmZkBQJqZmZmZmQlAZmZmZmZmBkBmZmZmZmYGQJqZmZmZmQVAZmZmZmZmCkCamZmZmZkJQGZmZmZmZgZAAAAAAAAACEBmZmZmZmYGQAAAAAAAAAhAZmZmZmZmBkBmZmZmZmYOQGZmZmZmZgZAZmZmZmZmBkDNzMzMzMwEQAAAAAAAAAhAMzMzMzMzC0DNzMzMzMwIQAAAAAAAAAhAzczMzMzMCEDNzMzMzMwIQM3MzMzMzAhAmpmZmZmZBUCamZmZmZkJQGZmZmZmZgpAAAAAAAAACEAAAAAAAAAEQAAAAAAAAAhAMzMzMzMzC0AAAAAAAAAIQA=="}}, {"axis": {"matches": true}, "label": "petal_length", "values": {"dtype": "f8", "bdata": "AAAAAAAAGEBmZmZmZmYUQJqZmZmZmRdAZmZmZmZmFkAzMzMzMzMXQGZmZmZmZhpAAAAAAAAAEkAzMzMzMzMZQDMzMzMzMxdAZmZmZmZmGEBmZmZmZmYUQDMzMzMzMxVAAAAAAAAAFkAAAAAAAAAUQGZmZmZmZhRAMzMzMzMzFUAAAAAAAAAWQM3MzMzMzBpAmpmZmZmZG0AAAAAAAAAUQM3MzMzMzBZAmpmZmZmZE0DNzMzMzMwaQJqZmZmZmRNAzczMzMzMFkAAAAAAAAAYQDMzMzMzMxNAmpmZmZmZE0BmZmZmZmYWQDMzMzMzMxdAZmZmZmZmGECamZmZmZkZQGZmZmZmZhZAZmZmZmZmFEBmZmZmZmYWQGZmZmZmZhhAZmZmZmZmFkAAAAAAAAAWQDMzMzMzMxNAmpmZmZmZFUBmZmZmZmYWQGZmZmZmZhRAZmZmZmZmFECamZmZmZkXQM3MzMzMzBZAzczMzMzMFEAAAAAAAAAUQM3MzMzMzBRAmpmZmZmZFUBmZmZmZmYUQA=="}}, {"axis": {"matches": true}, "label": "petal_width", "values": {"dtype": "f8", "bdata": "AAAAAAAABEBmZmZmZmb+P83MzMzMzABAzczMzMzM/D+amZmZmZkBQM3MzMzMzABAMzMzMzMz+z/NzMzMzMz8P83MzMzMzPw/AAAAAAAABEAAAAAAAAAAQGZmZmZmZv4/zczMzMzMAEAAAAAAAAAAQDMzMzMzMwNAZmZmZmZmAkDNzMzMzMz8P5qZmZmZmQFAZmZmZmZmAkAAAAAAAAD4P2ZmZmZmZgJAAAAAAAAAAEAAAAAAAAAAQM3MzMzMzPw/zczMzMzMAEDNzMzMzMz8P83MzMzMzPw/zczMzMzM/D/NzMzMzMwAQJqZmZmZmfk/ZmZmZmZm/j8AAAAAAAAAQJqZmZmZmQFAAAAAAAAA+D9mZmZmZmb2P2ZmZmZmZgJAMzMzMzMzA0DNzMzMzMz8P83MzMzMzPw/zczMzMzMAEAzMzMzMzMDQGZmZmZmZgJAZmZmZmZm/j9mZmZmZmYCQAAAAAAAAARAZmZmZmZmAkBmZmZmZmb+PwAAAAAAAABAZmZmZmZmAkDNzMzMzMz8Pw=="}}], "hovertemplate": "species=virginica<br>%{xaxis.title.text}=%{x}<br>%{yaxis.title.text}=%{y}<extra></extra>", "legendgroup": "virginica", "marker": {"color": "#00cc96", "symbol": "circle"}, "name": "virginica", "showlegend": true, "type": "splom"}];
var LAYOUT = {"legend": {"title": {"text": "species"}, "tracegroupgap": 0}, "margin": {"t": 60}, "dragmode": "select", "xaxis": {"matches": "y"}, "xaxis2": {"matches": "y2"}, "xaxis3": {"matches": "y3"}, "xaxis4": {"matches": "y4"}, "height": 900, "width": 750, "selections": [{"x0": 3, "x1": 4, "xref": "x2", "y0": 8, "y1": 6, "yref": "y"}, {"x0": 5, "x1": 1, "xref": "x3", "y0": 5, "y1": 4, "yref": "y"}]};
function clone(o) { return JSON.parse(JSON.stringify(o)); }
function cb(gd) { return JSON.stringify(gd.data[1].marker.colorbar || {}); }
function ax(gd) { return gd._fullLayout.xaxis ? Math.round(gd._fullLayout.xaxis._length) + 'px' : '?'; }
function dump(gd, el) {
  var cbs = gd.querySelectorAll('.colorbar');
  var w = cbs.length ? Math.round(cbs[cbs.length-1].getBoundingClientRect().width) + 'px' : 'none';
  el.textContent = 'trace1.marker.colorbar = ' + cb(gd) +
                   '\npanel width (xaxis._length) = ' + ax(gd) +
                   '\nrendered colorbar width = ' + w;
}

// Path A
var gdA = document.getElementById('A');
var dataA = clone(DATA);
dataA[1].marker.colorbar = { thicknessmode: 'pixels' };
Plotly.newPlot(gdA, dataA, clone(LAYOUT)).then(function () {
  dump(gdA, document.getElementById('mA'));
});

// Path B
var gdB = document.getElementById('B');
var dataB = clone(DATA);
dataB[1].marker.colorbar = {};
Plotly.newPlot(gdB, dataB, clone(LAYOUT)).then(function () {
  dump(gdB, document.getElementById('mB'));
  return Plotly.restyle(gdB, { 'marker.colorbar.thicknessmode': 'pixels' }, [1]);
}).then(function () {
  dump(gdB, document.getElementById('mB'));
});
</script>
</body>
</html>

Actual vs expected (measured with plotly 3.8.2, headless Chrome)

Path A (set at newPlot) Path B (set via restyle)
trace[1].marker.colorbar after render {"thicknessmode":"pixels"} {"thickness":16290,"thicknessmode":"pixels"}
panel width (_fullLayout.xaxis._length) 125 px 15 px
rendered colorbar width 71 px 16331 px

Expected: Path B behaves like Path A — thickness defaulted to ~30 px for pixels mode,
panels stay 125 px.

(Note: with random synthetic splom data the exact numbers differ — e.g. thickness:17250,
axis len ~15 — but the collapse reproduces identically.)

Analysis

colorbar.thickness is size-dependent when left at its default: in fraction mode the
default is ~30 / plotHeight (a small fraction), converted to pixels only at calc time.
When Plotly.restyle flips thicknessmode to pixels on a colorbar that still holds the
fraction-mode default, the default is apparently not re-coerced for the new mode — the
old tiny fraction survives, is interpreted as raw pixels on the next colorbar calc, and the
result (~16k px) is also written back into gd.data (user-visible trace), not just
_fullLayout.

Evidence that fits this reading:

  1. Restyling thickness together with thicknessmode
    ({'marker.colorbar.thickness': 20, 'marker.colorbar.thicknessmode': 'pixels'}) works
    fine — an explicit value bypasses the buggy default path.
  2. After Path B, the wrong value is observable on the user data object
    (gd.data[1].marker.colorbar.thickness === 16290), so a defaults-coercion result leaks
    back into gd.data.

First reported at plotly/plotly.py#5615;
the plotly.py maintainers asked for a pure-JS reproduction here.

Workaround

Include an explicit thickness in the same restyle call:

Plotly.restyle(gd, {
  'marker.colorbar.thicknessmode': 'pixels',
  'marker.colorbar.thickness': 20
}, [1]);

Activity

  1. LeonxLJX commented on Sep 3, 2026

    @LeonxLJX
    Author

    Root cause found and fix proposed in #8015.

    TL;DR: the mode-switch rescale in src/plot_api/plot_api.js fires whenever oldVal !== newVal — including when the user simply never set thicknessmode — and then multiplies innerContFull.thickness by gs.w as if it were a fraction. But since the attribute default is 'pixels', the value it rescales is already in pixels, so 30 * gs.w (e.g. 17700 at gs.w = 590) lands in the user data and the colorbar eats the plot. Verified headlessly on the 3.5.0 min bundle (heatmap shows it too — not splom-specific, splom just makes the collapse maximally visible).

    The same block also reads innerContFull.orient, which doesn't exist (the attribute is orientation, 'v'/'h'), so horizontal colorbars were normalizing against the wrong plot dimension all along.

  2. emilykl commented on Sep 4, 2026

    @emilykl
    Contributor

    Hi @LeonxLJX, thanks for the investigation. We have limited time and many PRs to review, so we are prioritizing the most serious and frequently-reported issues, and this issue so far is neither. We will get to reviews eventually but please be patient.

    The simpler the reproduction code, the easier it is for us to review. Please try to provide the simplest reproductions possible. Here is a much simpler way of reproducing the same issue (Clicking the "Do restyle" button triggers the bug and results in a too-wide colorbar):

    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <title>plotly.js splom + marker.colorbar restyle bug</title>
    <script src="https://cdn.plot.ly/plotly-4.0.0.min.js"></script>
    </head>
    <body>
    <div class="row">
        <h3>Div div1</h3>
        <div id="div1" style="width:750px;height:900px"></div>
      </div>
    </div>
    
    <script>
    var data = [
      {
        type: 'scatter',
        mode: 'markers',
        x: [1, 2, 3],
        y: [1, 2, 3],
        marker: {
          color: [1, 2, 3],
          colorbar: {
            x: 1.05
          }
        }
      }
    ];
    var layout = {};
    
    var gd = document.getElementById('div1');
    
    function doRestyle() {
      Plotly.restyle(gd, { 'marker.colorbar.thicknessmode': 'pixels' }, [0]);
    }
    
    Plotly.newPlot(gd, data, layout);
    </script>
    <button onclick="doRestyle()">Do restyle</button>
    </body>
    </html>

    I'm still unclear on where the 16290 value is coming from, could you provide a clear answer to that question? That will determine the most appropriate fix.

  3. changed the title [-][BUG] splom layout collapses when marker.colorbar.thicknessmode='pixels' is applied via Plotly.restyle (colorbar thickness silently becomes ~16k)[/-] [+][BUG] Colorbar thickness becomes too wide (~16k px) when marker.colorbar.thicknessmode='pixels' is applied via Plotly.restyle[/+] on Sep 4, 2026
  4. scenerecap commented on Sep 7, 2026

    @scenerecap

    Still reproduces on plotly.js 4.0.0 today (2026-09-07, Chromium 151):

    1. Create a scatter trace with marker.colorbar = {} (no explicit thickness or thicknessmode), render with Plotly.newPlot.
    2. Call Plotly.restyle(gd, { 'marker.colorbar.thicknessmode': 'pixels' }, [0]).

    The colorbar thickness in gd._fullData[0].marker.colorbar jumps to ~27 600 px (chart-width-dependent), the colorbar fills the entire plot container, and the scatter area collapses to a ~15 px strip on the left margin.

    Recording with timestamps: https://scenerecap.com/s/g4wpquv8ld

    (#8015 has a proposed fix, unmerged as of today.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions