> ## Documentation Index
> Fetch the complete documentation index at: https://docs.graphy.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Interactivity

Charts are interactive by default. Hovering highlights the nearest observation and shows a tooltip; no configuration is needed.

```tsx theme={null}
<GraphProvider spec={spec} data={data}>
  <GraphRenderer /> {/* hover + tooltips on by default */}
</GraphProvider>
```

How the highlight and the de-emphasis around it are painted is a [style](/sdk-next/config/styling) question — see [Restyling hover and dimming](#restyling-hover-and-dimming) below.

## How hover works

Hit-testing runs in the engine, and which query answers depends on the geom:

* **Banded and bucketed cartesian geoms** — bars, lines, areas — snap along the **main axis**: the coordinate the categories or buckets run along, which is x for a vertical bar or line chart and y for a horizontal bar chart. The cursor never has to land exactly on an observation, which is why a line chart responds smoothly between vertices. Everything at that main-axis position highlights together — every segment in a stack, every series in a multi-line chart — and the tooltip lists their values in legend order.
* **Point layers** — scatter — use nearest-neighbour in 2D, so the closest marker wins rather than the nearest column.
* **Polar charts** — pie and donut — hit-test the enclosing arc: the slice the cursor is inside.
* **Grid layers** — [heatmaps](/sdk-next/graph-types/heatmap) — hit-test the enclosing cell, over its full band rather than the inset tile painted inside it. Cells the data leaves empty answer no hover.

[Custom geoms](/sdk-next/extending/geom-renderers) answer their own hit-test queries.

Layers can opt out with `interactive: false` — useful for a decorative point overlay that shouldn't compete with the line beneath it:

```tsx theme={null}
geom.point({ interactive: false });
```

A non-interactive layer is filtered out before both hit-testing and grouping, so it never wins a hover **and** never appears as a companion row in another layer's tooltip. It is visual decoration as far as hover is concerned.

Marker diameter is a style, not a geom parameter:

```tsx theme={null}
styles({ defaults: [style.geom.point({ size: 6 })] });
```

## Restyling hover and dimming

Hover and de-emphasis are painted through two style **states**, `hovered` and `dimmed`, that any geom entry can scope to. `dimmed` covers both cases where an element steps back — a [highlight](/sdk-next/emphasis/highlights) matched something else, or the pointer is on another element. States are paint-only: they never feed layout, so nothing moves when the pointer does.

```tsx theme={null}
styles({
  overrides: [
    style.geom({ alpha: 0.15 }, { state: 'dimmed' }),
    style.geom.bar({ stroke: '#0B5FFF', strokeWidth: 2 }, { state: 'hovered' }),
  ],
});
```

Out of the box, `dimmed` drops a geom to `alpha: 0.4`, and hovered bars, points, and tiles gain a white affordance border. See [Styling](/sdk-next/config/styling) for the full cascade.

## Tooltips

Tooltips are on by default. `config.tooltip.mode` chooses which hovered observations the tooltip lists:

* `'band'` (default) lists everything at the hovered position: every segment of a stack, every bar of a group, and the related observation on each other layer.
* `'observation'` lists only what was hovered, and from each other layer the observation in the same group. In a grouped bar chart with error bars, hovering one bar shows that bar and its interval.
* `'none'` shows no tooltip. Hover, highlight and the cursor guide still respond.

```ts theme={null}
config({ tooltip: { mode: 'observation' } });
```

`showTooltips={false}` on the renderer hides the tooltip whatever the spec says:

```tsx theme={null}
<GraphRenderer showTooltips={false} />
```

### A layer's rows

Each layer contributes its geom's rows: the hovered value, labelled by its colour group, or the fields a geom declares (a candlestick's open, high, low and close). A layer's `tooltip` adds, removes, renames, reorders and formats them. Its keys name an aesthetic the layer maps, a variable the geom derives, or one of the geom's own fields, and a layer's default rows are named `y`. Your entries come first, in their order, then the geom's remaining rows:

```ts theme={null}
geom.point({
  aes: { size: 'population' },
  tooltip: {
    size: { title: 'Population', format: { type: 'integer' } },
    y: { format: { type: 'currency', iso: 'usd' } },
    color: { heading: true },
  },
});
```

* `false` removes a row and `true` adds or moves it.
* `title` replaces the row's label, and `format` its value format. Values are formatted in the chart's locale. The default rows keep their colour group's label, so a `title` on `y` names them only on a layer without colour groups.
* `heading: true` heads the tooltip with the field instead of the hovered category.

A key the layer can't read is dropped with an `UNKNOWN_TOOLTIP_FIELD` warning, and the chart still renders.

### Appearance

To change how the tooltip looks, replace it with the `Tooltip` [slot](/sdk-next/extending/slots). It receives `TooltipSlotProps` — a single `content` prop carrying the render-ready rows the default tooltip gets, already formatted. Positioning stays built in. `DefaultTooltip` is exported, so a custom tooltip can wrap it rather than rebuild it:

```tsx theme={null}
import { DefaultTooltip, type TooltipSlotProps } from '@graphysdk/react';

const MyTooltip = ({ content }: TooltipSlotProps) => (
  <div className="my-tooltip-shell">
    <DefaultTooltip content={content} />
  </div>
);

<GraphRenderer slots={{ Tooltip: MyTooltip }} />;
```

## Animation

A graph animates by default: an intro entrance on first mount (or, with `trigger: 'inView'`, when its
panel is in view), and transitions between compiled states when the data or a config value changes. Pass `animation` to turn either kind off, or to tune the intro:

```tsx theme={null}
<GraphRenderer animation={false} />
<GraphRenderer animation={{ transitions: false }} />
<GraphRenderer animation={{ intro: { stagger: false, durationScale: 2 } }} />
<GraphRenderer animation={{ intro: { trigger: 'inView' } }} />
```

The intro takes these settings:

<ParamField path="enabled" type="boolean" default="true">
  Whether the entrance plays at all. Passing `intro: false` is the same thing.
</ParamField>

<ParamField path="trigger" type="'mount' | 'inView'" default="mount">
  When the entrance starts. `inView` waits until 40% of the panel is on
  screen, so a graph below the fold plays it as the reader scrolls to it.
  Until then only the title, axes and pill legends draw. It plays once, and
  on mount where the browser has no `IntersectionObserver`. Use `mount` for a
  panel that may never be 40% on screen: one clipped by its container, one
  taller than two and a half screens, or a printed page.
</ParamField>

<ParamField path="durationScale" type="number" default="1">
  Multiplier applied to every entrance duration and stagger delay.
</ParamField>

<ParamField path="stagger" type="boolean" default="true">
  Whether geoms that support a staggered entrance enter one after another rather
  than all at once.
</ParamField>

<ParamField path="staggerOrder" type="IntroStaggerOrder" default="main-axis">
  The order staggered point geoms enter in. Bars and slices always enter in
  visual order.
</ParamField>

A viewer who prefers reduced motion gets no animation whatever this asks for.

### Transitions

<ParamField path="transitions" type="boolean" default="true">
  Whether geoms animate to their new position when the data or a config value
  changes. Set on `animation` itself, beside `intro`: turning one off leaves the
  other running.
</ParamField>

### Dense charts

<ParamField path="maxAnimatedGeoms" type="number" default="1500">
  Total geom count across all layers above which the graph plays no animation,
  intro or transitions. A line or area is one geom however many points it draws.
  Set on `animation` itself, beside `intro` and `transitions`.
</ParamField>

## Related

* [Styling](/sdk-next/config/styling) — the `hovered` and `dimmed` states, and everything else the chart paints
* [Slots](/sdk-next/extending/slots) — replace the tooltip or other regions
* [Provider & renderer](/sdk-next/rendering/provider-and-renderer) — `showTooltips`, `animation`, `mode`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.