Skip to content

Tooltip

ninejs lets you easily customize the tooltip, which appears when hovering over an element, using HTML and CSS.

Default tooltip style

By default, a tooltip looks like this:

import polars as pl
from plotnine import (
    ggplot,
    aes,
    geom_point,
    labs,
    facet_wrap,
    theme_minimal,
    theme,
    element_text,
    scale_fill_manual,
)
from ninejs import interactive, save, css

df = pl.read_csv(
    "https://github.com/y-sunflower/plotjs/blob/main/plotjs/data/titanic.csv?raw=true"
).with_columns(
    pl.when(pl.col("Survived") == 1)
    .then(pl.lit("Survived"))
    .otherwise(pl.lit("Did Not Survive"))
    .alias("Survival Status")
)

gg = (
    ggplot(df, aes(x="Age", y="Fare", fill="Survival Status", tooltip="Name"))
    + geom_point(size=8, alpha=0.5, show_legend=False)
    + facet_wrap("Survival Status")
    + scale_fill_manual(values={"Survived": "#4C78A8", "Did Not Survive": "#F58518"})
    + labs(x="Age", y="Fare")
    + theme_minimal()
    + theme(
        strip_text=element_text(weight="bold", size=12),
        plot_title=element_text(weight="bold", size=16),
    )
)

interactive(gg) + save("docs/iframes/tooltip-default.html")

Change background color and text color

We can make it look better with CSS. If you're completely unfamiliar with CSS, check out this other guide.

(
    interactive(gg)
    + css(".tooltip {background-color: #FFC300; color: #421173;}")
    + save("docs/iframes/tooltip-custom-css.html")
)

Make the text bigger

We can make the text bigger too:

(
    interactive(gg)
    + css(
        from_dict={
            ".tooltip": {
                "background-color": "#FFC300",
                "color": "#421173",
                "font-size": "1.5em",
            }
        }
    )
    + save("docs/iframes/tooltip-custom-css2.html")
)

Tip

AI tools are very good at CSS. Describe what you want, and they can help generate it.

Tooltips on aggregated charts with after_stat()

Some geoms don't draw your rows directly: geom_histogram, for example, first bins the data, so there is one bar per bin instead of one element per row. Mapping a raw column to tooltip can't work there — you would have one label per source row but far fewer bars.

The solution is plotnine's after_stat(): it maps the tooltip to the values computed by the stat, which are aligned one-to-one with the drawn elements.

from plotnine import (
    ggplot,
    aes,
    after_stat,
    geom_histogram,
    labs,
    theme_minimal,
    theme,
    element_text,
    element_blank,
)
from plotnine.data import diamonds

from ninejs import interactive, save

gg = (
    ggplot(
        diamonds,
        aes(
            x="carat",
            tooltip=after_stat(
                "'<b>' + count.astype(int).astype(str) + ' diamonds</b><br>'"
                " + xmin.round(2).astype(str) + ' to '"
                " + xmax.round(2).astype(str) + ' carats'"
            ),
        ),
    )
    + geom_histogram(binwidth=0.25, boundary=0, fill="#2a78d6", color="#ffffff")
    + labs(
        title="Most diamonds are under one carat",
        x="Carat",
        y="Number of diamonds",
    )
    + theme_minimal()
    + theme(
        plot_title=element_text(weight="bold", size=16),
        panel_grid_minor=element_blank(),
    )
)

interactive(gg) + save("docs/iframes/tooltip-after-stat.html")

Tip

This works with any stat, not just histograms: for example geom_bar() counts observations per category with stat_count, so tooltip=after_stat("count.astype(int).astype(str) + ' rows'") labels each bar with its count.

HTML injection inside the tooltip

df = pl.DataFrame(
    {
        "x": [1, 2, 3, 4, 5] * 2,
        "y": [2, 4, 3, 6, 5, 1, 3, 2, 5, 4],
        "category": ["Group A"] * 5 + ["Group B"] * 5,
    }
).with_columns(
    pl.format(
        """
        <i>The</i> <b>category</b> is <span style="color:red; font-size:24px;">{}</span>
        """,
        pl.col("category"),
    ).alias("tooltip")
)


gg = (
    ggplot(df, aes(x="x", y="y", color="category", tooltip="tooltip"))
    + geom_line(size=5)
    + theme_minimal()
    + labs(x="x", y="y", color="Category")
)

interactive(gg) + save("docs/iframes/tooltip-html-injection.html")

Note that the HTML inside the tooltip is made "safe" via DOMPurify, which removes any <script> or onclick events for security reasons. If you want to add JavaScript, see the dedicated guide.