Chart Tooltip
- Stable
- WCAG 2.2 evidence
- RTL
The content of a chart's hover and focus tooltip: a card with a caption and rows of marker, label
and value. Presentational and content-only: it renders in React Server Components, does not position itself and has no
behaviour. The chart (client code) decides when it is shown and where; useChartTooltip (same folder) implements the
show, hide, Escape and hoverable behaviour and placeChartTooltip the position. The root is role="tooltip". Pass an id and point the data point's aria-describedby at it while it is shown
(WCAG 1.3.1, 4.1.2), pass style (for example position: absolute with left and top) and className to position
it, and ref to measure it. Rows are a description list (term = series, definition = value). The series markers differ by
shape or dash as well as colour, like the legend's (WCAG 1.4.1). Long labels wrap. The card does not capture the pointer
away, so the pointer can move onto it (WCAG 1.4.13); the same values are in the chart's data table. Consumer duties: never keep information only in a tooltip (the table has it), format values for the locale, keep the series
wording identical to the legend, and close the tooltip with Escape.
import { ChartTooltip } from "@nexera-ui/react";- 6
- examples
- 3
- props
- 2
- live controls
- 1
- platform
- 9
- WCAG criteria
- 1
- block uses it
Try every prop. Copy the code.
Change the props and the code updates. Check light and dark, LTR and RTL, and phone width.
import { ChartTooltip } from "@nexera-ui/react";
<ChartTooltip />Examples 5
The same examples as Storybook, rendered live. Open Code to copy one.
Series markers
Markers of the eight series and the swatch kinds. Every series has a different glyph or dash as well as a different colour (WCAG 1.4.1); the glyphs repeat after four series, so give long series lists a legend with hasMarker lines.
import { ChartTooltip } from "@nexera-ui/react";
export function SeriesMarkers() {
return (
<div className="flex flex-wrap items-start gap-4">
{(["dot", "square", "line", "dashed"] as const).map((shape) => (
<ChartTooltip
key={shape}
title={`shape="${shape}"`}
rows={Array.from({ length: 8 }, (_, seriesIndex) => ({
seriesIndex,
shape,
label: `Series ${String(seriesIndex + 1)}`,
value: String((seriesIndex + 1) * 12),
}))}
/>
))}
</div>
);
}
Title rows and note
Rows are optional, and children takes free content after them (a note, a total).
import { ChartTooltip } from "@nexera-ui/react";
const figmaRows: readonly ChartTooltipRow[] = [
{ seriesIndex: 0, label: "Headcount", value: "121" },
{ seriesIndex: 7, label: "Hires", value: "6" },
];
export function TitleRowsAndNote() {
return (
<div className="flex flex-wrap items-start gap-4">
<ChartTooltip title="Only a caption" />
<ChartTooltip rows={[{ label: "Total", value: "1,204" }]} />
<ChartTooltip title="Q3 2026" rows={figmaRows}>
Includes 4 contractors.
</ChartTooltip>
</div>
);
}
Long labels
Long captions and labels wrap inside the 20 rem maximum width instead of overflowing.
import { ChartTooltip } from "@nexera-ui/react";
<ChartTooltip
title="Week 32, the week of the annual company offsite in Lisbon"
rows={[
{
seriesIndex: 0,
label: "Average time to hire across all departments and locations",
value: "34 days",
},
{ seriesIndex: 1, shape: "square", label: "Offers accepted", value: "81 %" },
]}
/>;
Right to left
Right-to-left: the card, the padding and the rows mirror; the digits follow the locale.
import { ChartTooltip } from "@nexera-ui/react";
<ChartTooltip
title="أغسطس 2026"
rows={[
{ seriesIndex: 0, label: "عدد الموظفين", value: "١٢١" },
{ seriesIndex: 1, label: "التعيينات", value: "٦" },
]}
/>;
On data points
Shows the pieces a chart combines: useChartTooltip (hover, focus, Escape, hoverable), placeChartTooltip (flip and clamp) and the ChartTooltip card. Tab into the points, move with the arrow keys, press Escape to dismiss, hover a point and move the pointer onto the card. A real chart also draws axes and a data table; this is only the tooltip wiring.
import { useRef, useState, type KeyboardEvent } from "react";
import { ChartTooltip, placeChartTooltip, useChartTooltip } from "@nexera-ui/react";
const DATA = [96, 104, 99, 112, 121, 128] as const;
const MONTHS = ["Mar", "Apr", "May", "Jun", "Jul", "Aug"] as const;
const WIDTH = 480;
const HEIGHT = 160;
const PAD = 24;
export function OnDataPoints() {
const tooltip = useChartTooltip();
const [stop, setStop] = useState(0);
const points = useRef<(SVGCircleElement | null)[]>([]);
const [tooltipRef, tooltipSize] = useContainerSize({ width: 0, height: 0 });
const x = (index: number) => PAD + (index * (WIDTH - 2 * PAD)) / (DATA.length - 1);
const y = scaleLinear([90, 130], [HEIGHT - PAD, PAD]);
const active = tooltip.activeKey;
const placement =
active === null
? null
: placeChartTooltip({
anchor: { x: x(active), y: y(DATA[active] ?? 0) },
tooltip: tooltipSize,
bounds: { width: WIDTH, height: HEIGHT },
});
const move = (event: KeyboardEvent, index: number) => {
const next =
event.key === "ArrowRight"
? Math.min(index + 1, DATA.length - 1)
: event.key === "ArrowLeft"
? Math.max(index - 1, 0)
: index;
if (next === index) return;
event.preventDefault();
setStop(next);
points.current[next]?.focus();
};
const style = seriesStyle(0);
return (
<div className="relative" style={{ width: WIDTH, height: HEIGHT }}>
<svg
role="group"
aria-label="Headcount, last six months"
width={WIDTH}
height={HEIGHT}
direction="ltr"
>
<path
d={linePath(DATA.map((value, index) => ({ x: x(index), y: y(value) })))}
fill="none"
strokeWidth={2.5}
className={style.stroke}
/>
{DATA.map((value, index) => (
<circle
key={MONTHS[index]}
ref={(node) => {
points.current[index] = node;
}}
role="img"
aria-label={`${MONTHS[index] ?? ""} 2026, Headcount ${String(value)}`}
tabIndex={index === stop ? 0 : -1}
cx={x(index)}
cy={y(value)}
r={active === index ? 6 : 4}
className={`${style.fill} outline-hidden focus-visible:stroke-primary focus-visible:stroke-2`}
onKeyDown={(event) => {
move(event, index);
}}
{...tooltip.getTriggerProps(index)}
/>
))}
</svg>
{active !== null && placement !== null ? (
<ChartTooltip
{...tooltip.tooltipProps}
ref={tooltipRef}
title={`${MONTHS[active] ?? ""} 2026`}
rows={[{ seriesIndex: 0, label: "Headcount", value: String(DATA[active] ?? 0) }]}
style={{ position: "absolute", left: placement.left, top: placement.top }}
/>
) : null}
</div>
);
}
Props 3
Press "Try it" on a card to load that prop into the playground.
3 props shown
titleNexeraReactNodeCaption of the data point: the date, category or x value the rows belong to. Shown when provided.
rowsNexerareadonly ChartTooltipRow[]Rows of the card. Shown after the title when not empty.
childrenNexeraReactNodeFree content shown after the rows, for what a row cannot say (a note, a link-free explanation).
* Required. React Aria props shown are the ones most apps use; the component accepts the rest of its React Aria props too.
Accessible by default.
Built on React Aria, and covered by the WCAG 2.2 evidence generated on every build.
WCAG 2.2 evidence
5 direct · 4 supporting- 1.1.1Non-text ContentLevel A · tested directly
- 1.3.1Info and RelationshipsLevel A · tested directly
- 1.3.2Meaningful SequenceLevel A · supporting test
- 1.4.1Use of ColorLevel A · supporting test
- 1.4.10ReflowLevel AA · supporting test
- 1.4.12Text SpacingLevel AA · supporting test
- 1.4.13Content on Hover or FocusLevel AA · tested directly
- 2.1.1KeyboardLevel A · tested directly
- 4.1.2Name, Role, ValueLevel A · tested directly
Fits any width.
Nexera components respond to the space they are given. Drag the corner of the frame, or pick a width.
Styling hooks
Pass className to add Tailwind classes (merged last). State is exposed as data attributes, so you can style it with variants like data-pressed:.
<ChartTooltip className="shadow-sm" />Used in blocks
Related components
- AccordionItemOne collapsible section of an FAQ or settings page.
- AreaChartA stacked area chart: layers piled up over time with the title, range switch, actions and legend of the card.
- AvailabilityRowOne person's availability across a window of the day: an avatar with initials and the name, and a timeline with the busy periods drawn as bars.
- AvatarA person or organisation shown as a photo, initials or an icon.
- AvatarGroupA row of overlapping avatars with a "+N" chip for the rest.
- AvatarLabelAn avatar with a name and an optional subtitle beside it, for a person in a list, a header or a mention.
- BadgeShort status or category label: `Tone` x `Variant` x `Size`, with an optional dot, leading icon and remove button.
- BadgeGroupA pill that pairs a small badge with a short message, for an announcement or a status line.