Hover to show
A useful area chart interaction is to show subcategories when hovering over a given category. This keeps the chart visually light while still giving readers the option to see more detail.
This mainly works because of reverse_hover, which lets you reverse the hover effects: hovering over a plot element now hides that element instead of hiding all the others.
import pandas as pd
from plotnine import (
aes,
geom_area,
geom_text,
ggplot,
position_stack,
scale_fill_manual,
theme_minimal,
)
from ninejs import css, interactive, javascript, save
from ninejs.data import revenue
aggregate = revenue.groupby(["month", "group"], as_index=False)["value"].sum()
label_data = revenue[revenue["month"] == pd.Timestamp("2025-07-01 00:00:00")]
colors = {
"Online subscriptions": "#56B4E9",
"Online services": "#0072B2",
"Retail stores": "#E69F00",
"Retail partners": "#D55E00",
"Online revenue": "#009E73",
"Retail revenue": "#CC79A7",
}
gg = (
ggplot(revenue, aes(tooltip="category", data_id="group"))
+ geom_area(aes(x="month", y="value", fill="category", group="category"), alpha=0.9)
+ geom_text(
label_data,
aes(x="month", y="value", label="category", group="category"),
inherit_aes=False,
position=position_stack(vjust=0.3),
color="white",
size=9,
)
+ geom_area(
aggregate,
aes(
x="month",
y="value",
fill="group",
group="group",
tooltip="group",
data_id="group",
),
alpha=1,
)
+ scale_fill_manual(values=colors, breaks=["Online revenue", "Retail revenue"])
+ theme_minimal()
)
(
interactive(gg, reverse_hover=True)
+ css(from_file="docs/examples/area_hover_to_show.css")
+ javascript(from_file="docs/examples/area_hover_to_show.js")
+ save("docs/iframes/area-hover-to-show.html")
)
#plot-container {
--default-not-hovered-opacity: 0;
}
.area {
transition: opacity 150ms ease;
}
svg g[id^="text_"] {
pointer-events: none;
}
.area.detail-layer {
opacity: 0;
pointer-events: none;
}
.area.detail-layer.not-hovered {
opacity: 1;
}
.area.aggregate-layer.not-hovered {
opacity: 0;
}
.tooltip {
display: none;
}
const plotData = JSON.parse(document.getElementById("plot-data").textContent);
const areas = plotData.axes?.axes_1?.areas ?? {};
const labels = areas.tooltip_labels ?? [];
const groups = areas.tooltip_groups ?? [];
document.querySelectorAll(".area").forEach((area, i) => {
const label = String(labels[i] ?? "");
const group = String(groups[i] ?? "");
area.classList.add(label === group ? "aggregate-layer" : "detail-layer");
});