Interaction reference

Searchable interaction contract. Chart-local state and callbacks by default. createPlotInteraction() for shared semantic state across plots and UI.

Static default

No capture layer, tooltip, selection, or zoom until a capability is enabled. Page scroll is not hijacked by unused tools.

Capability props

inspect

Prefer the declaration child <Inspect /> (or <Inspect mode="x" pin />, etc.). Options match the legacy GGPlot prop: mode, pin, maxDistance, content, contentMode, muteSiblings, tooltipTotal. Empty <Inspect /> equals inspect={true}. The GGPlot inspect prop still works. The heading id stays inspect for stable deep links.

Point selection

select={{ type: "point", multiple: true }} stores stable semantic keys. Identity defaults to an id column or row index; override with select={{ type: "point", identity: "…" }} or <Inspect identity="…" />.

Interval selection

select={{ type: "interval", mode: "x" | "y" | "xy", persistent: true }} enables an explicit Select area tool and emits domain and pixel bounds. In facets, add preset: "independent" | "union" | "cross-panel" to replace one panel, combine panel selections, or project one domain through compatible panels.

zoom

zoom={{ mode: "x" | "y" | "xy" }} enables the explicit Zoom area tool. Reset zoom and double-click return to the natural domains.

legendFocus

Prefer <GuideLegend channel="color" focus /> (boolean or { preview?: boolean }) for discrete legend preview and committed focus on that aesthetic. Host-only — never a PortableSpec / guideLegend() field. Use focus={{ preview: false }} to disable hover/focus preview while retaining click, touch, Enter, Space, Escape, and arrow-key controls. Requires stable row key values; continuous ramps stay static.

The plot prop legendFocus={true} is deprecated since 0.19.0 (removed in 0.20.0) and still enables focus plot-wide during the dual-read window — see Legend focus on GuideLegend.

legendFilter

Prefer <GuideLegend channel="color" filter /> (boolean or { mode?: "exclude" | "include", multiple?: boolean }) for data-changing checkboxes on that aesthetic's discrete legend. Host-only — never a PortableSpec / guideLegend() field. It changes the rows supplied to facets, statistics, scales, and rendering while preserving the full legend catalog and categorical color identity. Receive typed clauses through onlegendfilter. Independent of presentation-only GuideLegend focus.

The plot prop legendFilter={true} is deprecated since 0.19.0 (removed in 0.20.0) and still enables filter plot-wide during the dual-read window — see Legend filter on GuideLegend.

Controlled tool

tool and ontoolchange control the active Inspect, Select area, or Zoom area mode. Keep the value in Svelte state when application controls and the plot tool rail must stay synchronized:

<script lang="ts">
  import { GGPlot, Inspect, type InteractionTool } from "@ggts-sh/svelte";

  let activeTool = $state<InteractionTool>("inspect");
</script>

<GGPlot
  select={{ type: "interval" }}
  tool={activeTool}
  ontoolchange={(next) => (activeTool = next)}
>
  <Inspect />
</GGPlot>

A controlled unavailable tool requests a change and emits a diagnostic; it does not silently arm a different drag behavior. The active tool remains local to one chart; shared controllers coordinate data semantics, not UI modes.

Shared controller

createPlotInteraction<Key>({ onchange? }) returns a reactive PlotInteractionController<Key>. Pass it through the interaction prop and name the semantic channels with the required interactionScope={{ keys, x?, y? }}; controlled plots never fall back to a generic scope or infer x/y channel names from encodings. Controlled zoom requires an explicit scope for every active channel (x, y, or both).

  • Reads: selected(scope), emphasized(scope), intervals(scope), isSelected(key, scope), zoom(scope), snapshot, and revision.
  • Selection: setSelection, toggleSelection, and clearSelection.
  • Lightweight presentation: setEmphasis and clearEmphasis.
  • Facet intervals: setInterval, clearInterval, and clearIntervals.
  • Domains: setZoom and resetZoom for finite numeric x/y pairs.
  • Data replacement: reconcileKeys(validKeys, { scope }) explicitly removes selected or emphasized keys that no longer exist.

Scopes are application-level names. Reuse a key scope only where keys mean the same thing, and reuse x/y scopes only where their data domains are compatible. Every mutation returns one immutable transition or null for a no-op. Passive consumers never republish controller state, preventing linked-view feedback loops. Do not mutate the controller inside its synchronous onchange callback; schedule a later Svelte application update instead. See the linked views example.

Identity

key is a field name or accessor returning a unique stable PropertyKey. Public events expose semantic keys, aggregate sourceKeys, and lineageCount, never renderer indices.

Events

oninspect

Receives PlotInspection: change with transient or pinned focus and members, or clear.

onselect

Receives PlotSelection. Point selection emits end and clear. Interval selection emits start, change, end, and clear.

onzoom

Receives ZoomEvent: end with explicit domains or clear with null domains.

onlegendfocus

Receives LegendFocusEvent: a transient or committed change carrying the raw encoded value, formatted label, scale channel, and stable row keys, or a small clear event. The same object is included in oninteraction.

onlegendfilter

Receives LegendFilterEvent: a change with one typed LegendFilterClause, or clear with clause: null. Legend filtering changes pipeline input and is not folded into the presentation interaction union.

oninteraction

Receives the same discriminated PlotInteractionEvent union emitted by the focused callbacks. Narrow on type and phase.

ondiagnostic

Receives structured PlotDiagnostic objects (InteractionDiagnostic or DeprecationDiagnostic) with severity, code, message, prop, suggestions, and docUrl. Deprecation advisories also carry since and removeIn.

<GGPlot
  ondiagnostic={(diagnostic) =>
    console.warn(diagnostic.code, diagnostic.message, diagnostic.suggestions)}
/>

Every event has a source: pointer, keyboard, touch, or programmatic.

Diagnostics

INTERACTION_INTERVAL_FACET_UNSUPPORTED

Brush zoom currently requires one unfaceted panel.

INTERACTION_INVALID_MAX_DISTANCE

inspect.maxDistance must be a finite non-negative CSS-pixel distance.

INTERACTION_POINT_REQUIRES_KEY

Durable point selection requires resolved row identity.

INTERACTION_INTERVAL_PRESET_REQUIRES_KEY

Coordinated interval presets (union, cross-panel) require resolved row identity; without it they combine no rows.

INTERACTION_INVALID_KEY

A key accessor returned null, undefined, or a non-PropertyKey value.

INTERACTION_DUPLICATE_KEY

The key accessor returned a duplicate value; durable interaction is disabled for that value.

INTERACTION_UNSTABLE_KEY

The key accessor returned a different value for the same source row.

INTERACTION_MISSING_LINEAGE

A synthetic or aggregate mark did not expose source-row lineage.

INTERACTION_LEGEND_REQUIRES_KEY

Legend focus requires resolved row identity so encoded legend values never become identities.

INTERACTION_LEGEND_DISCRETE_ONLY

Legend focus currently applies to discrete color and fill legends; continuous ramps remain static.

INTERACTION_INTERVAL_SCALE_UNSUPPORTED

Interval domains and brush zoom require continuous linear, log, or time scales.

INTERACTION_TOOL_UNAVAILABLE

The requested interaction tool is unavailable for the enabled capabilities.

INTERACTION_SCOPE_WITHOUT_CONTROLLER

interactionScope is ignored without an interaction controller; chart-local scope is derived from key and aes.

INTERACTION_HANDLER_WITHOUT_CAPABILITY

An interaction handler is set but its capability prop is not enabled, so the handler never fires.

INTERACTION_INSPECT_X_ON_COL

inspect.mode x/xy draws a crosshair on the x (band) axis through column marks; columns already encode x as a filled band, so the guide cuts the bar body and rarely adds information. Under coord_flip the guide is horizontal but still tracks the band.

INTERACTION_INSPECT_X_ON_BAR

inspect.mode x/xy draws a crosshair through bar marks; bars are filled regions on the band axis, so the guide cuts the bar body and rarely adds information. Under coord_flip the guide orientation swaps with the axes but still fights the marks.

INTERACTION_INSPECT_X_BISECTS_COL_LABELS

inspect.mode x/xy draws a crosshair through GeomCol marks that also carry GeomText/GeomLabel values; the guide bisects the on-bar totals and makes them hard to read (including under coord_flip, when the guide is horizontal).

INTERACTION_INSPECT_X_BISECTS_BAR_LABELS

inspect.mode x/xy draws a crosshair through GeomBar marks that also carry GeomText/GeomLabel values; the guide bisects the on-bar totals and makes them hard to read (including under coord_flip).

INTERACTION_INSPECT_AXIS_ON_VIOLIN

inspect.mode draws an axis guide through violin marks; violins sit on a discrete band, so freescrolling x/y/xy guides cut the density body and often leave the band tooltip row blank.

INTERACTION_INSPECT_AXIS_ON_BOXPLOT

inspect.mode draws an axis guide through boxplot marks; boxes sit on a discrete band, so freescrolling x/y/xy guides cut the box body and rarely add information.

INTERACTION_INSPECT_AXIS_ON_ERRORBAR

inspect.mode draws an axis guide through errorbar marks; interval geoms on a discrete band are better inspected on the mark itself than via a freescrolling guide.

INTERACTION_INSPECT_AXIS_ON_LINERANGE

inspect.mode draws an axis guide through linerange marks; interval geoms on a discrete band are better inspected on the mark itself than via a freescrolling guide.

INTERACTION_INSPECT_AXIS_ON_POINTRANGE

inspect.mode draws an axis guide through pointrange marks; interval geoms on a discrete band are better inspected on the mark itself than via a freescrolling guide.

INTERACTION_INSPECT_AXIS_ON_CROSSBAR

inspect.mode draws an axis guide through crossbar marks; even when hits pin to a category, freescrolling x/y/xy guides rarely add information beyond exact mark focus.

INTERACTION_INSPECT_HIGH_CARDINALITY_DISCRETE

Inspect is enabled with a high-cardinality discrete color/fill domain; the default tooltip shows the focused series, the largest contributors at that x (or y), a stack total, and an overflow line — not every series.

INTERACTION_INSPECT_IDENTITY_DROPPED

An <Inspect> child replaced the inspect prop whole (REPLACE), so the prop's identity no longer applies; rows fall back to an id column or row index.

INTERACTION_DUPLICATE_INSPECT_CAPABILITY

Multiple <Inspect> children are registered; only the last one's options apply (REPLACE).

Accessibility

The plot surface is named and keyboard focusable when interaction is enabled. Arrow keys or brackets traverse data; Enter or Space pins or commits the active tool; Escape dismisses. A polite live region announces concise state while pinned HTML remains labelled, navigable DOM. Area tools remain explicit so ordinary page scrolling is available until a user chooses a drag mode.

Committed interval and zoom state exposes precise Edit-bounds buttons in the tool rail. Their inline form uses labelled native inputs, stages drafts until Apply, validates log/time/category constraints, restores trigger focus after Apply or Cancel, and supports Escape. Linear and reversed domains use ascending data values; time uses ISO 8601 text; band intervals use inclusive native selects. Clear panel selection, Clear all selections, Reset zoom, and Reset legend filters remain separate operations.