šŸ§ˆ Buttery Smooth Tooltips in Dash

Ever wanted your tooltips to smoothly glide across your plots?

Well, now they can :slight_smile:

medium AdobeExpress

How it Works

The main ingredients:

  1. Use dcc.Tooltip instead of the native tooltip feature
  2. Apply a transition property to the .dcc-tooltip-bounding-box
  3. Keep the tooltip always visible for smoothness

A Limitation The tooltip will always stay hovering over the last point you moved over. In other words, the "show" property is never getting modified, unlike the example this example is based off: see this dopage

This was done to reduce flickering from disappearing and revealing the tooltip when there is no hoverData and when there is hoverData respectively.

The Code

Below, you can find the the code snippets that put this example together, in the files app.py and styles.css, which is contained in an assets/ folder in the project.

You can also find the code at GitHub.

app.py

import random

import dash
from dash import dcc, html, Input, Output, State
import plotly
import plotly.graph_objects as go


app = dash.Dash(
    __name__,
    suppress_callback_exceptions=True,
)

def gen_plotly_figure():
    avail_vals = list(range(20))
    x_arr = []
    y_arr = []
    for x in avail_vals:
        for y in avail_vals:
            plot_pts = random.random() > 0.6
            if plot_pts:
                x_arr.append(x)
                y_arr.append(y)

    my_figure = go.Figure(
        data=[
            go.Scatter(
                x=x_arr,
                y=y_arr,
                mode="markers",
                marker=dict(color="#1C1D42"),
                hoverinfo="none",
            ),
        ],
        layout={
            "height": 600,
            "width": 600,
            "plot_bgcolor": "#fafafa",
            "paper_bgcolor": "#fafafa",
            "xaxis": {
                "color": "#f3f3f3",
                "tickfont": {
                    "color": "#666666",
                },
                "gridcolor": "#f3f3f3",
            },
            "yaxis": {
                "color": "#f3f3f3",
                "tickfont": {
                    "color": "#666666",
                },
                "gridcolor": "#f3f3f3",
            },
        },
    )
    return my_figure


def layout():
    return html.Div(
        [
            dcc.Graph(
                id="my-figure", className="my-figure", figure=gen_plotly_figure()
            ),
            dcc.Tooltip(
                id="my-tooltip",
                className="my-tooltip",
                loading_text="",
            ),
        ]
    )
app.layout = layout


@app.callback(
    Output("my-tooltip", "bbox"),
    Output("my-tooltip", "children"),
    Input("my-figure", "hoverData"),
)
def update_tooltip(hoverData):
    if not hoverData:
        return dash.no_update, dash.no_update

    pt = hoverData["points"][0]
    bbox = pt["bbox"]

    tooltip_content = html.Div(
        [
            html.P(f"X: {pt['x']}"),
            html.P(f"Y: {pt['y']}"),
        ]
    )
    return bbox, tooltip_content


if __name__ == "__main__":
    app.run_server(debug=True)

assets/style.css

:root {
    --tooltip-color: #FFEEBC;
}

body {
    background-color: #fafafa;
    font-family: Verdana;
}

.dcc-tooltip-bounding-box {
    transition: 0.23s;
    animation-timing-function: cubic;
}

.dcc-tooltip-bounding-box .hover::after {
    border-color: transparent var(--tooltip-color) transparent transparent !important;
}

.my-tooltip {
    color: #222;
    font-size: 0.9rem;
    background-color: var(--tooltip-color) !important;
    border: 0px solid red !important;
    height: 4rem !important;
    width: 3rem !important;
    box-shadow: 0 0 20px rgba(0,0,0,0.08) !important;
}

.my-tooltip * {
    padding: 0 !important;
    margin: 0 !important;
}
3 Likes

Hey @Adam,

This is cool! It is indeed smooth.

One thing Iā€™d change about your style sheet, when using the class names, you would be applying these settings to all tooltips. You could just apply it to the one by using the id instead, like you did in the place lower.

2 Likes

Indeed, a very nice tooltip experience. Thank you for sharing, @Adam

1 Like

Hey Adam,

For hover tips that take a bit to load, the loading message is showing for me. Iā€™m getting a flicker when moving between traces as the loading message is set to " " . Is there a way to have no loading message?