Description
hex_to_rgb() assumes the hex string is always 6 digits. It computes the channel width as len(value) // 3 and slices the string into equal thirds:
defhex_to_rgb(value):
value=value.lstrip("#")
hex_total_length=len(value)
rgb_section_length=hex_total_length//3returntuple(
int(value[i : i+rgb_section_length], 16)
foriinrange(0, hex_total_length, rgb_section_length)
)For the 3-digit CSS shorthand form this takes each digit as a whole channel value instead of expanding it (CSS says #FFF == #FFFFFF, i.e. each digit is doubled). So:
hex_to_rgb("#FFF") # -> (15, 15, 15) near-blackhex_to_rgb("#FFFFFF") # -> (255, 255, 255) whiteThose are the same colour in CSS, but produce wildly different results. Nothing raises - the wrong colour is returned silently.
This matters because plotly itself treats #FFF as a perfectly valid colour:
go.Figure(go.Scatter(x=[1], y=[1], marker=dict(color="#FFF"))) # accepted, renders white
so users can reasonably pass shorthand hex, and the figure factories that route colours through hex_to_rgb then misinterpret them (figure_factory/_annotated_heatmap.py:155, _ternary_contour.py:248, _trisurf.py:120, matplotlylib/mpltools.py:136).
Visible consequence:create_annotated_heatmap picks the annotation font colour from the background luminance via should_use_black_text(). With shorthand hex, a white background is read as (15, 15, 15), so it chooses white text on a white background and the annotations become invisible:
| colorscale | resulting annotation font colour |
|---|
[[0, "#FFF"], [1, "#FFF"]] | #FFFFFF (invisible on white) |
[[0, "#FFFFFF"], [1, "#FFFFFF"]] | #000000 (correct) |
A fix would be to expand the 3-digit form before parsing (and probably reject lengths that are neither 3 nor 6, rather than silently splitting into thirds - e.g. a 4 or 5 character string currently produces nonsense too).
Steps to reproduce
importplotly.figure_factoryasfffromplotly.colorsimporthex_to_rgbprint(hex_to_rgb("#FFF")) # (15, 15, 15) <- expected (255, 255, 255)print(hex_to_rgb("#FFFFFF")) # (255, 255, 255)z= [[1, 2], [3, 4]]
fig_short=ff.create_annotated_heatmap(z, colorscale=[[0, "#FFF"], [1, "#FFF"]])
print({a.font.colorforainfig_short.layout.annotations}) # {'#FFFFFF'} -> white on whitefig_long=ff.create_annotated_heatmap(z, colorscale=[[0, "#FFFFFF"], [1, "#FFFFFF"]])
print({a.font.colorforainfig_long.layout.annotations}) # {'#000000'} -> correctBoth colorscales describe the same white background, but only the 6-digit form produces readable annotations.
Happy to open a PR for this if it's welcome.
Versions
- plotly 6.7.0
- Python 3.14.3
- Windows 11
Description
hex_to_rgb()assumes the hex string is always 6 digits. It computes the channel width aslen(value) // 3and slices the string into equal thirds:For the 3-digit CSS shorthand form this takes each digit as a whole channel value instead of expanding it (CSS says
#FFF==#FFFFFF, i.e. each digit is doubled). So:Those are the same colour in CSS, but produce wildly different results. Nothing raises - the wrong colour is returned silently.
This matters because plotly itself treats
#FFFas a perfectly valid colour:so users can reasonably pass shorthand hex, and the figure factories that route colours through
hex_to_rgbthen misinterpret them (figure_factory/_annotated_heatmap.py:155,_ternary_contour.py:248,_trisurf.py:120,matplotlylib/mpltools.py:136).Visible consequence:
create_annotated_heatmappicks the annotation font colour from the background luminance viashould_use_black_text(). With shorthand hex, a white background is read as(15, 15, 15), so it chooses white text on a white background and the annotations become invisible:[[0, "#FFF"], [1, "#FFF"]]#FFFFFF(invisible on white)[[0, "#FFFFFF"], [1, "#FFFFFF"]]#000000(correct)A fix would be to expand the 3-digit form before parsing (and probably reject lengths that are neither 3 nor 6, rather than silently splitting into thirds - e.g. a 4 or 5 character string currently produces nonsense too).
Steps to reproduce
Both colorscales describe the same white background, but only the 6-digit form produces readable annotations.
Happy to open a PR for this if it's welcome.
Versions