From f0073e07775cc49fd28f7f0c7a3b85ba816660b7 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Wed, 2 Sep 2026 19:45:18 +0000 Subject: [PATCH 1/4] Export tick label colors to the plotly tickfont --- plotly/matplotlylib/mplexporter/utils.py | 5 ++++- plotly/matplotlylib/mpltools.py | 10 ++++++++-- plotly/matplotlylib/tests/test_renderer.py | 22 ++++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/plotly/matplotlylib/mplexporter/utils.py b/plotly/matplotlylib/mplexporter/utils.py index 8170ced8f0..e4ce206db3 100644 --- a/plotly/matplotlylib/mplexporter/utils.py +++ b/plotly/matplotlylib/mplexporter/utils.py @@ -239,12 +239,15 @@ def get_axis_properties(axis): # Get axis scale props["scale"] = axis.get_scale() - # Get major tick label size (assumes that's all we really care about!) + # Get major tick label size and color (assumes that's all we really + # care about!) labels = axis.get_ticklabels() if labels: props["fontsize"] = labels[0].get_fontsize() + props["fontcolor"] = export_color(labels[0].get_color()) else: props["fontsize"] = None + props["fontcolor"] = None # Get associated grid props["grid"] = get_grid_style(axis) diff --git a/plotly/matplotlylib/mpltools.py b/plotly/matplotlylib/mpltools.py index ad01b37520..4ec640175e 100644 --- a/plotly/matplotlylib/mpltools.py +++ b/plotly/matplotlylib/mpltools.py @@ -517,7 +517,10 @@ def prep_xy_axis(ax, props, x_bounds, y_bounds): showgrid=props["axes"][0]["grid"]["gridOn"], domain=convert_x_domain(props["bounds"], x_bounds), side=props["axes"][0]["position"], - tickfont=dict(size=props["axes"][0]["fontsize"]), + tickfont=dict( + size=props["axes"][0]["fontsize"], + color=props["axes"][0]["fontcolor"], + ), ) xaxis.update(prep_ticks(ax, 0, "x", props)) yaxis = dict( @@ -526,7 +529,10 @@ def prep_xy_axis(ax, props, x_bounds, y_bounds): showgrid=props["axes"][1]["grid"]["gridOn"], domain=convert_y_domain(props["bounds"], y_bounds), side=props["axes"][1]["position"], - tickfont=dict(size=props["axes"][1]["fontsize"]), + tickfont=dict( + size=props["axes"][1]["fontsize"], + color=props["axes"][1]["fontcolor"], + ), ) yaxis.update(prep_ticks(ax, 1, "y", props)) return xaxis, yaxis diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 18ce2d02b3..142309fc81 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -351,3 +351,25 @@ def test_custom_date_xtickvals_given_as_numbers_are_converted(): "2023-01-07 00:00:00", "2023-01-10 00:00:00", ) + + +def test_tick_label_color_exports(): + """Tick label colors are exported to the plotly tickfont.""" + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.xaxis.tickfont.color == "#000000" + + +def test_dark_tick_label_color_exports(): + """Dark-background tick label colors are exported to the plotly + tickfont.""" + with plt.style.context("dark_background"): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.xaxis.tickfont.color == "#FFFFFF" From 90ed8fd9608d14411b3580254dc242238522d50b Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Sun, 4 Oct 2026 17:53:00 +0000 Subject: [PATCH 2/4] Map transparent tick label colors to rgba in tickfont --- plotly/matplotlylib/mpltools.py | 19 +++++++++++++++++-- plotly/matplotlylib/renderer.py | 12 +----------- plotly/matplotlylib/tests/test_renderer.py | 12 ++++++++++++ 3 files changed, 30 insertions(+), 13 deletions(-) diff --git a/plotly/matplotlylib/mpltools.py b/plotly/matplotlylib/mpltools.py index 4ec640175e..30f7a28476 100644 --- a/plotly/matplotlylib/mpltools.py +++ b/plotly/matplotlylib/mpltools.py @@ -510,6 +510,21 @@ def prep_ticks(ax, index, ax_type, props): return axis_dict +def _export_color(color): + """Export a matplotlib color for use as a plotly color. + + matplotlib uses "none" for fully transparent colors, which plotly does not + accept, so transparent colors are exported as transparent black. + Colors already exported by the mplexporter (hex or rgba strings) are + passed through unchanged. + """ + if color is None: + return None + if isinstance(color, str): + return color if color != "none" else "rgba(0,0,0,0)" + return [_export_color(c) for c in color] + + def prep_xy_axis(ax, props, x_bounds, y_bounds): xaxis = dict( type=props["axes"][0]["scale"], @@ -519,7 +534,7 @@ def prep_xy_axis(ax, props, x_bounds, y_bounds): side=props["axes"][0]["position"], tickfont=dict( size=props["axes"][0]["fontsize"], - color=props["axes"][0]["fontcolor"], + color=_export_color(props["axes"][0]["fontcolor"]), ), ) xaxis.update(prep_ticks(ax, 0, "x", props)) @@ -531,7 +546,7 @@ def prep_xy_axis(ax, props, x_bounds, y_bounds): side=props["axes"][1]["position"], tickfont=dict( size=props["axes"][1]["fontsize"], - color=props["axes"][1]["fontcolor"], + color=_export_color(props["axes"][1]["fontcolor"]), ), ) yaxis.update(prep_ticks(ax, 1, "y", props)) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 65bbcfabb1..57a466b80c 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -14,17 +14,7 @@ from plotly.matplotlylib import mpltools -def _export_color(color): - """Export a matplotlib color for use as a plotly color. - - matplotlib uses "none" for fully transparent colors, which plotly does not - accept, so transparent colors are exported as transparent black. - Colors already exported by the mplexporter (hex or rgba strings) are - passed through unchanged. - """ - if isinstance(color, str): - return "rgba(0,0,0,0)" if color == "none" else color - return [_export_color(c) for c in color] +from plotly.matplotlylib.mpltools import _export_color class PlotlyRenderer(Renderer): diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 142309fc81..dbd8e03191 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -373,3 +373,15 @@ def test_dark_tick_label_color_exports(): plotly_fig = tls.mpl_to_plotly(fig) assert plotly_fig.layout.xaxis.tickfont.color == "#FFFFFF" + + +def test_transparent_tick_label_color_exports(): + """Transparent tick label colors ('none') export as transparent rgba.""" + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + ax.tick_params(labelcolor="none") + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.xaxis.tickfont.color == "rgba(0,0,0,0)" + assert plotly_fig.layout.yaxis.tickfont.color == "rgba(0,0,0,0)" From 9d440d8719fe487f01a8ed6ad9b59516776ef966 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Sun, 4 Oct 2026 18:19:14 +0000 Subject: [PATCH 3/4] Add changelog entry for PR 5716 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0583fec45c..650358bd5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Added +- Export tick label colors to the plotly tickfont in `mpl_to_plotly` [[#5716](https://github.com/plotly/plotly.py/pull/5716)], with thanks to @robertoffmoura for the contribution! + ### Fixed - Fix concurrent first access to lazily initialized graph object properties, which could raise `ValueError("Invalid value")` [[#3441](https://github.com/plotly/plotly.py/issues/3441)], with thanks to @hb1915 for the contribution! - Fix `px.sunburst`, `px.treemap` and `px.icicle` listing sectors in a different order on every run when `path` is used with a Polars DataFrame; sectors now follow their order of first appearance for all dataframe backends [[#5765](https://github.com/plotly/plotly.py/issues/5765)], with thanks to @Irahan2 for the contribution! From e29313fe03a6892e37a76934c45042a75fc52528 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Mon, 5 Oct 2026 20:18:58 -0600 Subject: [PATCH 4/4] Update CHANGELOG --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb8e0a747e..346a1459c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,10 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased -### Added -- Export tick label colors to the plotly tickfont in `mpl_to_plotly` [[#5716](https://github.com/plotly/plotly.py/pull/5716)], with thanks to @robertoffmoura for the contribution! - ### Fixed - Fix concurrent first access to lazily initialized graph object properties, which could raise `ValueError("Invalid value")` [[#5691](https://github.com/plotly/plotly.py/pull/5691)], with thanks to @hb1915 for the contribution! - Fix `mpl_to_plotly` crashing on touching bars (such as `plt.hist`) due to floating-point noise producing negative `bargap` values by clamping `bargap` to `[0, 1]` [[#5696](https://github.com/plotly/plotly.py/pull/5696)], with thanks to @robertoffmoura for the contribution! +- Export tick label colors to the plotly tickfont in `mpl_to_plotly` [[#5716](https://github.com/plotly/plotly.py/pull/5716)], with thanks to @robertoffmoura for the contribution! - Fix `px.sunburst`, `px.treemap` and `px.icicle` listing sectors in a different order on every run when `path` is used with a Polars DataFrame; sectors now follow their order of first appearance for all dataframe backends [[#5766](https://github.com/plotly/plotly.py/pull/5766)], with thanks to @Irahan2 for the contribution! ## [7.1.0] - 2026-09-15