Skip to content

Line Chart API

Everything you need to wire up a line chart in code; for the story and demos, see the Line Chart demo.

Import

ts
import "@michi-vz/wc/line-chart";
// <michi-vz-line-chart> is now defined
ts
import { mountLineChart } from "@michi-vz/core";

const chart = mountLineChart(el, props);

Props

PropTypeDefaultDescription
timelineboolean | TimelinePeriodConfig-Opt-in "play through years" (cumulative): the marks draw UP TO the active period and extend as the timeline steps; `interpolate` sweeps smoothly between years, `false` jump-cuts. Headless controller via `chart.timeline()` plus the built-in play button + scrubber. Off by default; wins over `progressiveDraw` when both are set.
dataSet*LineDataItem[]-Array of line series, each with its own points
titlestring-Optional chart title rendered above the plot
yAxisDomain[number | null, number | null]-Fix the y-axis range as [min, max] instead of deriving it from the data. Either bound may be null to keep just that bound data-derived — e.g. [0, null] pins the baseline at 0 while the maximum keeps following the visible (post legend-toggle, post top-N) series.
yAxisScale"linear" | "log""linear"Y-axis scale: "linear" (default) or a base-10 "log" scale. In "log" mode, a non-positive value (<= 0) can't be plotted, so those points are dropped as missing (reported via `onDataWarning`); a dataSet with no positive values at all renders the no-data state instead.
xAxisDataType"date_annual" | "date_monthly" | "number""number"How x values are parsed and formatted: yearly dates, monthly dates, or plain numbers
xAxisFormat(d: number | string) => string-Formats an x tick value into its display label
yAxisFormat(d: number | string) => string-Formats a y tick value into its display label
ticksnumber5Approximate number of x-axis ticks to generate
yTicksnumber-Number of y-axis ticks (default 10, matching the legacy denser value axis).
showGridLinesboolean-Draw horizontal dashed grid lines at each y tick (default true).
showVerticalGridLinesboolean-Draw vertical dashed grid lines at each x tick (default false - the legacy chart drew none).
highlightZeroLineboolean-Emphasise the y=0 grid line with a darker solid stroke (default true).
tickValuesArray<number | Date>-Explicit tick values, overriding the generated ones
fillPeriodTicksboolean-Draw a tick for EVERY period across the axis range (every month/year), not just the periods present in the data. Periods with no value render faded and show `noDataTickTooltip` on hover. Opt-in; default false.
noDataTickTooltip(date: number) => string-Tooltip content (plain text or sanitized HTML) for a faded no-data tick; receives the tick's epoch-ms value. Used only with `fillPeriodTicks`. Default: a localized "Data not available".
noDataTickColorstring-Colour for faded no-data tick labels; sets the `--michi-vz-tick-nodata` CSS var on the host. Used only with `fillPeriodTicks`.
curve"curveBumpX" | "curveLinear" | "curveMonotoneX"-Default interpolation for every series (per-series `curve` wins).
detectGapsbooleanfalseAuto-derive `certainty` from missing periods (dashes the gap segment).
expectedStepnumber-Expected cadence in axis units; REQUIRED for xAxisDataType "number".
showDataPointsbooleanfalseWhether to draw a marker at each data point (default false)
enableMouseLineboolean | MouseLineConfigtrueSolid vertical crosshair line snapped to the nearest data point x on hover; pass a config object to style it (default true)
zoomboolean | LineZoomConfig-Opt-in x-axis drag-to-zoom: drag a horizontal range inside the plot to zoom into it (a selection rectangle previews the range). Marks clip to the plot box; axis ticks, crosshair snapping, and tooltips all follow the zoomed domain. The y-domain stays FULL (derived from all data, not the visible slice). While zoomed, a built-in "Reset zoom" button restores the full domain; `resetZoom()` / `setZoomDomain()` on the instance drive it programmatically. Off by default.
onZoomChange(domain: [number, number] | null) => void-Called when the zoom domain changes: the zoomed [min, max] in axis units (epoch ms on date axes), or null when reset to the full domain.
singlePointLineboolean | SinglePointLineConfig-true / config draws a horizontal guide line for single-point series.
fontFamilystring-Font family for axis/title/tooltip text (SVG + canvas). Sets the --michi-vz-font-family CSS var so both renderers resolve it.
filterFilter-Top-N / sort filter applied to the data before rendering
progressiveDrawboolean | ProgressiveDrawConfig-Animate each line drawing itself left to right on mount (progressive reveal). `true` uses defaults; pass a config to tune duration, easing, tip labels, and autoplay. Off by default, so existing charts are unchanged. Respects prefers-reduced-motion: the chart then renders fully drawn instantly. Ignored by the webgpu renderer.
isLoadingboolean-Show the loading overlay and skip the no-data check (legacy michi-vz parity).
isNodataboolean | ((dataSet: LineDataItem[] | null | undefined) => boolean)-No-data override: boolean, or a predicate on dataSet; default = empty/all-empty-series.
noDataLabelstring-Text for the vanilla default no-data overlay (ignored when suppressed).
suppressDefaultOverlayboolean-A framework wrapper sets this to render its OWN loading/no-data node instead.
tooltipFormatter(d: DataPoint, series: DataPoint[], dataSet: LineDataItem[]) => string-Returns custom tooltip HTML for a hovered datum (sanitized before it is inserted)
sharedTooltipbooleanfalseWhen true, hovering anywhere in the plot shows ONE tooltip listing every series' value at the nearest x (year) - the "shared"/crosshair tooltip - instead of the single nearest series. Pairs with the crosshair (enableMouseLine, on by default).
sharedTooltipFormatter(input: { x: number | string; xLabel: string; entries: Array<{ label: string; value: number; color: string; d: DataPoint }>; }) => string-Custom HTML for the shared tooltip. `entries` is one row per series that has a point at the hovered x, in dataSet order, each with its resolved colour.
onHighlightItem(labels: string[]) => void-Called when the hovered/highlighted label(s) change
svgChildrenstring-Pre-serialised SVG markup injected as direct <svg> children (axis-title text, reference lines). The React wrapper fills this from `children`; mirrors the legacy <LineChart>'s `{children}` slot.
Common props - shared by every chart (14)
PropTypeDefaultDescription
widthnumber1000Chart width in pixels
heightnumber500Chart height in pixels
marginMargin{ top: 50, right: 50, bottom: 50, left: 60 }Inner margins (top/right/bottom/left, in px); default 8 (36 top with a title)
colorsstring[]-Categorical palette for rings without an explicit colour or colorsMapping entry
colorsMappingRecord<string, string>-Explicit label -> colour map; takes precedence over the palette and per-item colours
highlightItemsstring[]-Labels to emphasise (active styling); others keep their base opacity
disabledItemsstring[]-Labels to hide entirely (ring + track removed; remaining rings keep their radii order)
renderer"svg" | "canvas" | "webgpu""svg"Render as inline SVG (default) or to a canvas / WebGPU layer; getContext() is identical either way
localestring-BCP-47 locale used for number formatting
skipColorMappingDispatchbooleanfalseExternal-CSS mode: unmapped labels resolve to transparent and onColorMappingGenerated is not emitted, so arc colours come from your CSS via the data-label-safe contract
enableTransitionsbooleantrueAnimate opacity/arc changes with CSS transitions (SVG renderer; default true)
onColorMappingGenerated(mapping: Record<string, string>) => void-Called with the resolved label -> colour map after the chart assigns colours
onChartDataProcessed(context: ChartContext) => void-Called with the renderer-agnostic ChartContext whenever the data is (re)processed
onDataWarning(warnings: DataWarning[]) => void-Called with any non-fatal data warnings (out-of-range values, duplicate labels, ...)

Grid and axis display

Four props control the y-axis tick density and grid line rendering:

PropDefaultNotes
yTicks10Approximate number of y-axis ticks. The legacy default was 10; set lower (e.g. 5) for a sparser axis.
showGridLinestrueHorizontal dashed grid lines at each y tick.
showVerticalGridLinesfalseVertical dashed grid lines at each x tick. The legacy chart drew none; opt in only when the extra guides help readability.
highlightZeroLinetrueDraws the y=0 line as a solid stroke (coloured by --michi-vz-zero-line, falling back to the grid colour) rather than a regular dashed tick. Useful when a dataset spans positive and negative values.

Loading and no-data state

The engine manages a data-mv-state attribute on the host element with three values - "loading", "nodata", and "ready" - and shows built-in overlays for the first two unless you opt out.

PropTypeDefaultNotes
isLoadingbooleanfalseShows the .mv-loading overlay and bypasses the no-data check entirely.
isNodataboolean | (dataSet) => boolean-Overrides the default predicate (empty dataSet or every series has zero points). Pass false to force the chart to render even when data looks empty.
noDataLabelstring-Text shown inside the default .mv-nodata overlay. Ignored when suppressDefaultOverlay is true.
suppressDefaultOverlaybooleanfalsePrevents the engine from injecting its own loading/no-data node. Use this when a framework wrapper (e.g. the @michi-vz/react LineChart) renders isLoadingComponent / isNodataComponent as a React overlay instead. The host is never unmounted - the overlay is layered on top.

React wrapper behaviour

@michi-vz/react's LineChart automatically sets suppressDefaultOverlay and renders isLoadingComponent / isNodataComponent as a positioned React node above the chart host. The chart DOM is always mounted, so isNodataComponent still fires on empty data even without a custom predicate.

Font family

fontFamily sets the CSS custom property --michi-vz-font-family on the host element, which is read by both the SVG text renderer and the canvas getComputedStyle probe. The family must already be loaded by the page - no font embedding is performed.

ChartContext / legendData

onChartDataProcessed receives a LineChartContext that extends BaseChartContext. The base now carries a legendData field:

ts
interface LegendItem {
  label: string;         // series label as it appears in dataSet
  color: string;         // resolved colour at the time of processing
  order: number;         // appearance order (legend slot index)
  disabled?: boolean;    // true when the label is currently hidden
  dataLabelSafe?: string; // sanitizeForClassName(label) - the CSS hook the canvas colour probe matches
}

interface BaseChartContext {
  // ... existing fields ...
  legendData?: LegendItem[]; // populated by LineChart; treat absence as []
}

legendData is the canonical payload for consumer colour authorities. A framework wrapper that drives its own colour CSS (e.g. thd MonitorV2's useChartUtils) reads legendData[].{label, dataLabelSafe, color, disabled} from each onChartDataProcessed call and emits per-label stroke/fill rules targeting the data-label-safe attribute. This replaces the need to cross-reference colorsMapping with the series order.

Events

The web component dispatches these bubbling CustomEvents (the engine exposes the same via the on* callbacks in the table above):

EventDetailFires when
michi-vz:highlightstring[]hover highlight changes
michi-vz:colormappingRecord<string, string>a color mapping is generated
michi-vz:dataprocessedChartContextdata is (re)processed
michi-vz:datawarningDataWarning[]input warnings are detected

getContext()

mountLineChart(el, props).getContext() returns a renderer-agnostic LineChartContext (structured stats + a deterministic natural-language summary + an a11y table). See LLM context.

Source

Props are typed as LineChartProps in @michi-vz/core.