Components/SparkChart

SparkChart

The static multi-series polyline inside a card: painted on canvas, with a DOM cursor and tooltip over it.

VerifiedSince 0.3.9

SparkChart

Basic Usage

SparkChart + ChartScrubber

A scrubbable trend snapshot

Two series, read by sweeping the pointer across them.

Loading demo...

Sizing and Theme

The chart fills its container and takes its size from that container rather than a prop, so the wrapper needs a definite height (166px in the example). A ResizeObserver repaints it when that size changes.

A canvas cannot read CSS variables, so a theme change has to trigger a repaint. theme defaults to 'auto' and follows both the data-theme attribute and the .dark class, on <html> or <body> — the two switches the tuffex token layer keys off. The bitmap is scaled by devicePixelRatio (capped at 2) while all drawing maths stays in CSS pixels.

ChartScrubber

The scrub layer is pure DOM and paints nothing into the canvas: that is what keeps the tooltip selectable and the cursor one pixel wide at any device pixel ratio. It is the outer element, and the chart goes in its default slot.

Pointer position is resolved against the container's width into the nearest sample index. The tooltip anchor is clamped between 28% and 72%, so it never overhangs the stage at either end. With no activeIndex bound the index is owned internally — it is a pure interaction transient, and most hosts only need @scrub.

The stage sets touch-action: pan-y: the horizontal axis belongs to the scrubber, the vertical one to the page.

API

SparkChart Props

NameTypeDefaultDescription
seriesSparkSeries[]Series list, { id, data, color?, label? }.
theme'light' | 'dark' | 'auto''auto'auto follows data-theme or .dark.
gridbooleanfalseDraws horizontal hairline gridlines.
gridLinesnumber4Number of gridlines.
lineWidthnumber2.25Stroke width in CSS pixels.
paddingPartial<SparkChartPadding>{ top: 24, right: 0, bottom: 22, left: 0 }Inner inset; omitted sides keep the default.
domain[number, number]Fixed value range; omit to fit the data.
ariaLabelstringAccessible name for the canvas. Without it the canvas is hidden from assistive tech.

SparkPoint is { time: number, value: number }; time only positions the sample horizontally and need not be a real timestamp.

SparkChart Exposed

MethodDescription
redraw()Repaints at once, for hosts that mutate series data in place.

ChartScrubber Props

NameTypeDefaultDescription
pointCountnumberHow many samples the pointer maps onto.
activeIndexnumber | nullControlled index. Omit to let the component own it.
rowsChartTooltipRow[]Tooltip rows, { label, value, color? }.
timeLabelstringCaption line above the rows.
tooltipbooleantrueTurn off for a bare cursor line.
anchorMin / anchorMaxnumber28 / 72Clamp range for the tooltip anchor, in percent.
disabledbooleanfalseIgnores the pointer.

ChartScrubber Events

EventPayloadDescription
scrub(index: number)The pointer reached a new sample. Never repeats for the same one.
leavePointer left, lifted, or was cancelled.
update:activeIndex(index: number | null)Controlled write-back.

ChartScrubber Slots

SlotScopeDescription
defaultStage content, normally TxSparkChart.
tooltip{ index, rows }Replaces the whole tooltip.

Interaction Contract

  • The chart draws no axes, no legend and no value labels: those belong to the card and are laid out by the host.
  • A series without color falls back through --tx-bui-accent / -orange / -green / -red by position. Upstream hard-codes those as dark-theme hex values, which read too bright in light mode; here they resolve through tokens.
  • All series share one value domain, and the x axis is derived from time; when every sample carries the same time it falls back to even index spacing. A single sample is centred and painted as a dot by the round line cap.
  • Empty data or a zero-sized container paints nothing and throws nothing. A host with no 2D context (jsdom, for instance) is skipped just as safely.
  • The scrub layer only emits when the index changes; upstream re-sets its state on every pointer move.
  • Neither component animates, so there is nothing for a reduced-motion guard to switch off.

Best Practices

  • Give the stage a definite height, or a zero-height container leaves the chart invisible.
  • Use aria-label to say what the chart claims; without it the canvas is marked aria-hidden so assistive tech does not announce an empty element. The numbers themselves should be readable in the tooltip or the surrounding copy.
  • Format tooltip values before passing them: the component does no number formatting, and a U+2212 minus () lines up under tabular figures where a hyphen does not.
  • To stream data, replace the series reference to trigger a repaint, or call redraw() after pushing in place.

Source

  • Component source: packages/tuffex/packages/components/src/spark-chart/src/TxSparkChart.vue, TxChartScrubber.vue.
  • Projection and painting: packages/tuffex/packages/components/src/spark-chart/src/geometry.ts, draw.ts.
  • Types: packages/tuffex/packages/components/src/spark-chart/src/types.ts.
  • Tested coverage: packages/tuffex/packages/components/src/spark-chart/__tests__/spark-chart.test.ts (27 cases) covers domain resolution, both time- and index-based x mapping, single-sample centring, anchor clamping, the draw-call sequence, scrub de-duplication and controlled mode.
  • Adapted from Beautiful UI (https://www.beautifului.dev), © 2026 Shane Levine, MIT.
查看源码
packages/tuffex/packages/components/src/spark-chart/index.ts