GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — add style customization to `hoverlabel` by Lexachoc · Pull Request #8115 · plotly/plotly.js

Conversation

@Lexachoc

@Lexachoc Lexachoc commented Oct 10, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Addresses some requests in #2342 without closing it.

This PR adds several new hoverlabel styling options based on the discussion in #2342.

New options:

  • hoverlabel.borderwidth — sets the hover label border width
  • hoverlabel.borderradius — sets the hover label border radius
  • hoverlabel.borderpad — sets the padding between the text and the border
  • hoverlabel.namecolor — sets the secondary hover label text color
  • hoverlabel.bgnamecolor — sets the secondary hover label background color
  • hoverlabel.shadow — sets a drop shadow on the hover label

Additional improvement

If hoverlabel.namecolor is not specified, the trace color is preserved when it has sufficient contrast against the secondary label background (WCAG 4.5:1). Otherwise, a contrasting text color is used automatically.

(Not sure whether this contrast improvement change belongs in this PR. Happy to move it out if preferred.)

Before/After

See the comparison in #2342 (comment).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Lexachoc added 2 commits October 10, 2026 17:41
@Lexachoc
add style customization to hoverlabel
48efa70
@Lexachoc
add draftlog
9a10c97
@Lexachoc Lexachoc mentioned this pull request Oct 10, 2026
More hover label style customizations #2342
Open
@Lexachoc Lexachoc changed the title add style customization to hoverlabel add style customization to hoverlabel Oct 10, 2026
Lexachoc added 2 commits October 10, 2026 18:04
@Lexachoc
revert accidental mock change
52afd34
@Lexachoc
improve default secondary hover label text contrast
1926941
Lexachoc
Lexachoc commented Oct 10, 2026
View reviewed changes
Comment thread draftlogs/8115_add.md Resolved

Uh oh!

There was an error while loading. Please reload this page.

@camdecoster camdecoster assigned KoolADE85 Oct 13, 2026
@camdecoster

camdecoster commented Oct 13, 2026

Copy link
Copy Markdown
Contributor

Thanks for the PR! Our team will review and follow up.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Lexachoc

Lexachoc commented Oct 14, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

BTW, I just realized that there are currently no controls for the width of either the main hover label or the secondary hover label, and there is also no automatic text wrapping.

This has actually been requested before:

Width / automatic wrapping

  • Automatic hover text wrapping #1964 — automatic hover text wrapping / hover text width
  • Fixed-width hover labels with hoverlabel.width #3800 — hoverlabel.width

#3800 was closed during the repository cleanup, but the requested hoverlabel.width feature was never implemented. In fact, it was previously suggested that adding hoverlabel.width with pixel values would be a reasonable addition.

I think it may make sense to add:

hoverlabel: {
    width: 200,
    maxwidth: 300
}

where width sets an explicit width, while maxwidth preserves the natural width up to the specified limit and automatically wraps text beyond it.
This should apply independently to both the main hover label and the secondary (<extra>) hover label.

Hover label positioning

While looking into this, I also noticed that the positioning problem I reported in #7330 is closely related to the much older issue #381:

  • Hovertemplate will block the point due to the lack of top and bottom side #7330
  • Display hover text on top instead of to the right #381

#381 has been open since 2016 and includes a proposal for: hoverlabel.position

with values such as:

 'auto', 
 'top left', 'top center', 'top right',
 'middle left', 'middle center', 'middle right',
 'bottom left', 'bottom center', 'bottom right',

ref: #381 (comment)

IMHO, I think options: "auto" | "top" | "right" | "bottom" | "left" would be enough for normal use cases.


Above may also be worth considering but in separate follow-ups.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@KoolADE85

KoolADE85 commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Hey @Lexachoc thanks for the submission here! I was about to review it, but noticed there are conflicts with a previous PR. I'll jump on this for a closer look after the conflicts are cleared up.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Lexachoc

Lexachoc commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Hey @Lexachoc thanks for the submission here! I was about to review it, but noticed there are conflicts with a previous PR. I'll jump on this for a closer look after the conflicts are cleared up.

Oops, no problem, I'll let you know once I've resolved the conflicts.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Lexachoc added 2 commits October 20, 2026 18:58
@Lexachoc
merge changes from PR8051 (MathJax hover rendering)
447e3e3
@Lexachoc
Merge remote-tracking branch 'upstream/main' into feature-hoverlabel-…
db89bb8
…custom-styles

# Conflicts:
#	src/components/fx/hover.js
#	test/jasmine/bundle_tests/mathjax_test.js
@Lexachoc

Lexachoc commented Oct 20, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor Author

@KoolADE85 I merged them.

You can play with it using the mock below:

{
  "data": [
    {
      "x": [1, 2, 3, 4],
      "y": [10, 15, 13, 17],
      "mode": "markers",
      "name": "$\\alpha_{1c} = 352 \\pm 11 \\text{ km s}^{-1}$",
      "type": "scatter",
      "marker": {
        "opacity": 0.5
      }
    },
    {
      "x": [2, 3, 4, 5],
      "y": [16, 5, 11, 9],
      "mode": "lines",
      "type": "scatter",
      "opacity": 0.5
    },
    {
      "x": [1, 2, 3, 4],
      "y": [12, 9, 15, 12],
      "mode": "lines+markers",
      "type": "scatter",
      "opacity": 0.707,
      "marker": {
        "opacity": 0.707
      }
    },
    {
      "x": [1, 2, 3],
      "y": [1, 2, 3],
      "opacity": 0.2,
      "line": { "width": 10, "color": "red" },
      "marker": { "size": 20, "color": "blue" }
    }
  ],
  "layout": {
    "title": {
      "text": "hovermode='closest'"
    },
    "hoverlabel": {
      "borderwidth": 5,
      "borderradius": 20,
      "namecolor": "red",
      "bgnamecolor": "yellow",
      "shadow": "0 10px 4px rgba(0, 0, 0, 0.2)",
      "borderpad": 15,
      "namelength": -1
    },
    "hovermode": "closest"
  }
}
image image

However, I noticed two issues:

  1. LaTeX positioning in hovermode: 'y':
    The rendered LaTeX expression is incorrectly positioned when using hovermode: 'y':
image

I reverted to the original hover.js from #8051 and reproduced the same issue:
image


  1. LaTeX not rendered in unified hover modes (existing limitation):

LaTeX expressions are not rendered when using hovermode: 'x unified' or hovermode: 'y unified':
image

image

I also reproduced this behavior using the original hover.js from #8051.

Both are reproducible with #8051's original implementation, so they do not appear to be caused by this merge.

KoolADE85 reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Lexachoc Lexachoc mentioned this pull request Oct 20, 2026
Render hover labels through MathJax when the whole label is one tex expression #8051
Merged
@Lexachoc
Merge branch 'main' into feature-hoverlabel-custom-styles
eb35573
@SergeyAxiomatic SergeyAxiomatic mentioned this pull request Oct 21, 2026
Fix tex hover label name truncation and rotated label placement #8138
Open
KoolADE85
KoolADE85 requested changes Oct 26, 2026
View reviewed changes

@KoolADE85 KoolADE85 left a comment

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You picked up quite a big chunk of work here @Lexachoc - great job!
I tested it out pretty thoroughly. I think there are some details to think through so that the new attrs look good in a variety of configurations.
But overall, this will be a great feature!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/components/fx/hover.js
Comment on lines +1971 to +1977
var nameBg = bgNameColor || Color.addOpacity(bgColor, 0.8);
if(nameColor === undefined) {
nameColor =
Color.wcagContrast(fallbackNameColor, nameBg) < 4.5
? Color.contrast(nameBg)
: fallbackNameColor;
}

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice that this contrast fix will alter existing graphs when their trace colours do not meet the contrast standard.
For that reason, I would prefer to pull that change out of this PR and into its own issue where we can discuss some approaches. For those users who are happy with the way their graphs look, I'm cautious to casually change the colour-coding behaviour.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@Lexachoc Lexachoc Oct 27, 2026

Copy link
Copy Markdown
Contributor Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I've opened #8149 for discussion. I'll remove the automatic contrast logic and test after addressing the other review comments.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

KoolADE85 reacted with heart emoji
All reactions
  • ❤️ 1 reaction
Comment thread src/components/fx/hover.js
Comment on lines +1780 to +1792
var borderPadDelta = unifiedBorderPad - HOVERTEXTPAD;
if(!unifiedBg.empty() && borderPadDelta) {
var bgX = Number(unifiedBg.attr('x')) || 0;
var bgY = Number(unifiedBg.attr('y')) || 0;
var bgWidth = Number(unifiedBg.attr('width')) || 0;
var bgHeight = Number(unifiedBg.attr('height')) || 0;

unifiedBg.attr({
x: bgX - borderPadDelta,
y: bgY - borderPadDelta,
width: Math.max(0, bgWidth + 2 * borderPadDelta),
height: Math.max(0, bgHeight + 2 * borderPadDelta)
});

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the "unified" hover modes, the positioning of the hover labels do not account for border padding. As a result, the popup will appear in an unexpected location.

Example with exaggerated padding (notice where the cursor is!):

Image

I think the unit tests could check for position here too, to catch any regressions.

Also on this topic: rounded corners are quite pixelated in this mode! This seems to be because the box is drawn with shape-rendering: crispEdges. I think we should use geometricPrecision or auto instead.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/components/fx/hover.js
Comment on lines +2085 to +2086
d.bx = tbbWidth + 2 * borderPad;
d.by = Math.max(tbbHeight + 2 * borderPad, tx2height);

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't take into account the border width, so it's possible for a thick border to clip some of the label text:

Image

Annotations include the border in their size: borderfull = borderwidth + borderpad (draw.js#L218).

This would be a good candidate for a unit test as well!

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/components/fx/hover.js
Comment on lines +2683 to 2689
var nameRect = g.select('rect').call(
Drawing.setRect,
pX(shiftX.text2ShiftX + ((shiftX.alignShift - 1) * d.tx2width) / 2 + offsetX),
pX(rectX),
pY(offsetY - d.by / 2 - 1),
pX(d.tx2width),
pX(rectWidth),
pY(d.by + 2)
);

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name box doesn't include the border or the radius which makes it look misshapen.

Without a stroke, the outline makes the name box appear to be a different size than the main box.
Example: (hoverlabel: { borderwidth: 8, borderradius: 20, bgnamecolor: 'black' })
Image


With borderradius, it gets a bit trickier. There is no border rounding where the main box joins with the name box. These corners are square and, when there isn't any bgnamecolor, it looks like the rounding only applied to half of the label

Example: hoverlabel: { borderwidth: 4, borderradius: 12, borderpad: 12 }
Image

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/components/fx/hover.js
Comment on lines +210 to +215
borderWidth: hoverItem.borderWidth,
borderRadius: hoverItem.borderRadius,
borderPad: hoverItem.borderPad,
nameColor: hoverItem.nameColor,
bgNameColor: hoverItem.bgNameColor,
shadow: hoverItem.shadow,

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These options are supported on every trace with hoverlabel but not in sankey. Can we also support sankey? #7451 had the same discussion for showarrow.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/components/fx/attributes.js
Comment on lines +17 to +34
borderwidth: extendFlat({}, hoverLabelAttrs.borderwidth, {
description: 'Sets the border width (in px) of the hover labels for this trace.'
}),
borderradius: extendFlat({}, hoverLabelAttrs.borderradius, {
description: 'Sets the border radius (in px) of the hover labels for this trace.'
}),
borderpad: extendFlat({}, hoverLabelAttrs.borderpad, {
description: 'Sets the padding (in px) between the text and the border of the hover labels for this trace.'
}),
namecolor: extendFlat({}, hoverLabelAttrs.namecolor, {
description: 'Sets the text color of the secondary hover label for this trace.'
}),
bgnamecolor: extendFlat({}, hoverLabelAttrs.bgnamecolor, {
description: 'Sets the background color of the secondary hover label for this trace.'
}),
shadow: extendFlat({}, hoverLabelAttrs.shadow, {
description: 'Sets the shadow of the hover labels for this trace.'
}),

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other attributes support arrayOk for per-point array values, but these ones are excluded. Is that intentional? What do you think about supporting arrays here?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/components/fx/hover.js
return path + 'Z';
}

function roundedSideArrowPath(width, height, horzSign, offsetX, offsetY, radius, roundOppositeSide, pX, pY) {

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rounded paths add three helpers (roundedRectPath, roundedSideArrowPath and roundedTopBottomArrowPath). The places that use these functions still keep the original hand-written paths using an if/else to decide which one to use.

Could we reduce duplication by creating a single helper that generically rounds the corners of a list of points?

Example fn signature:

function roundedPolygonPath(
  points: {x: number, y: number}[],
  radius?: number | number[] = 0,
  scaleX?: number = 1,
  scaleY?: number = 1
): string;

An array radius would be per vertex so the arrow corners can stay sharp.
A radius of 0 would be "square" (effectively a no-op for this fn).

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread test/jasmine/tests/hover_test.js

@KoolADE85 KoolADE85 Oct 26, 2026

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new tests only set the new hover options for the whole chart (layout.hoverlabel). Could we add a test that sets one on a single trace and checks it overrides the chart-wide value?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@Lexachoc Lexachoc mentioned this pull request Oct 27, 2026
[FEATURE]: Add automatic contrast for secondary hover labels #8149
Open
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@KoolADE85 KoolADE85 KoolADE85 requested changes

Requested changes must be addressed to merge this pull request.

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@KoolADE85 KoolADE85

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

3 participants

@Lexachoc @camdecoster @KoolADE85
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.