/docsfor

Interaction

Tooltips, hover and controlled highlight, clicks, and the mark address every chart stamps.

Charts stay server components. Interaction lives in diagrammatic/interactive, a client entry whose components wrap a chart and read the addresses it stamps on its marks. A tooltip or a click handler receives an address, never the data: you look the row up in the data you already have.

Weekly sessions by channelsearchdirectreferralsocialW1W2W3W4W5W6W7W8W9W10

Click a point to read its address.

fig. 01 · A controlled highlight driven by buttons, hover highlight by series, a tooltip keyed by index, and onMarkClick

The mark address

Every drawn mark carries data-part="mark" and up to four address slots. Each form fills the slots its marks have (a parallel-coordinates record has a series but no index), and no form adds a fifth, so the interaction API never grows per form.

SlotAttributeMeaning
indexdata-iThe primary ordinal: the point, bar, or category
index2data-i2The secondary ordinal: the row of a grid
seriesdata-seriesThe identity: a series, a node, a country, or a flow's source
series2data-series2The counterparty: a flow's or an edge's target

getMarkDatum(element) decodes an element into that address, plus the element itself. It is what Root uses, and it works on its own in any event handler you attach to the chart's root.

Root

Root is a div that delegates pointer events to the marks inside it.

PropDescription
onMarkHoverCalled with the datum when the pointer enters a mark, and with null when it leaves
onMarkClickCalled with the datum and the event on a mark click
highlightOnHovertrue, "index", or "series": what the hovered mark highlights. true tries index, then series
highlightA controlled MarkQuery or list of queries; while set, it wins over hover

A highlight stamps data-dg-active on matching marks and data-dg-muted on the rest. diagrammatic/styles.css fades muted marks; restyle either state with your own CSS. applyHighlight(container, query) is the same stamp as a pure function, for a host driving it without Root.

"use client";

import { useState } from "react";
import { Line, type Series } from "diagrammatic";
import { Root } from "diagrammatic/interactive";

export function TrafficChart({ series, weeks }: { series: Series[]; weeks: string[] }) {
  const [selected, setSelected] = useState<string | null>(null);
  return (
    <Root
      {...(selected && { highlight: { series: selected } })}
      highlightOnHover="series"
      onMarkClick={(datum) => setSelected(datum.series ?? null)}
    >
      <Line series={series} labels={weeks} />
    </Root>
  );
}

Tooltip

Tooltip renders inside Root and follows the pointer over the hovered mark. Its child is a render function that receives the datum.

PropDescriptionDefault
side"top", "bottom", "left", or "right""top"
sideOffsetDistance from the pointer in pixels10
collisionFlip to the other side and clamp inside the container when it would overflowtrue

Give the tooltip surface width: max-content (w-max in Tailwind), because an absolutely positioned box shrinks against its container's right edge and would change width as the pointer moves.

Benchmark scoreatlas-149.1nova46.3swift45.8atlas-040.2quill31.6pico27.9
fig. 02 · Root and Tooltip around a server-safe Bar

getSeriesColor(element, series) returns the color a series is drawn in, read from the live figure, so a tooltip swatch matches the chart under any theme.

Without the wrapper

Every native SVG attribute and event handler passes through to a chart's root element, and refs forward. CSS alone can do hover emphasis:

svg[data-dg]:has([data-part="mark"]:hover) [data-part="mark"]:not(:hover) {
  opacity: 0.35;
}

Accessibility

Marks are pointer affordances. The accessible path to the numbers is the chart's title and summary plus a table you render beside it; see Accessibility.