Gap Chart
Comparison
How far apart are the two numbers that matter? Plot before and after, target and actual, men and women, and the bar between them is the story - the wider the gap, the louder it reads.
The chart above is the same engine in every framework - only the integration code below differs.
When to reach for it
- Target vs actual, before vs after, forecast vs outturn. Two values per row where the distance between them is the headline - the gap bar IS the finding.
- Ranking by gap. Sort the rows and the biggest wins (or worst misses) surface instantly - built for the Monday-morning review of who closed their gap.
- If the absolute sizes matter more than the difference, side-by-side sub-bars on a Comparable bar keep both magnitudes readable.
Heavy data on WebGPU Experimental
GapChart has an opt-in renderer="webgpu" that paints the value1/value2 markers and connecting bars as GPU-instanced shapes while axes, labels and tooltips stay on the SVG layer. It is capability-gated: on a browser without WebGPU it downgrades to canvas automatically, and getContext().renderer reports whichever actually painted.
Play through the years
Give every row a date and flip on timeline: the chart becomes a year-by-year story with its own play button and scrubber, snapshotting one period at a time. Off by default - nothing changes until a chart opts in.
Press the play button under the chart: it steps through the years, one snapshot at a time. Drag the scrubber to jump to any year.
const ref = useRef<GapChartHandle>(null);
<GapChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / stepForward()<GapChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" /><div use:gapChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>applyGapChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });<michi-vz-gap-chart id="c"></michi-vz-gap-chart>
<script>
const el = document.getElementById("c");
el.timeline = { speedMs: 1000, loop: true };
// el.getTimeline() -> play() / pause() / seek(year)
</script>speedMssets the pace,loopwraps around,autoplay: truestarts on mount,showControl: falsehides the built-in bar.- Values glide between periods by default (
interpolate); tune the motion withtweenMsandeasing, or setinterpolate: falsefor hard cuts. Reduced motion always gets the hard cut. - The headless controller is always available:
chart.timeline()exposesplay() / pause() / toggle() / seek(period) / stepForward() / stepBack(), plusonStepandformatPeriodin the config for custom UI. - A
filter(top-N, sorting) still applies inside each period, so a "top 5 per year" race works out of the box. - Rows without a
datestay visible in every period.
Usage
import { GapChart } from "@michi-vz/react";
export default () => <GapChart {...props} />; // props = the chart options<script setup>
import { GapChart } from "@michi-vz/vue";
</script>
<template>
<GapChart :options="props" />
</template><script>
import { gapChart } from "@michi-vz/svelte";
</script>
<div use:gapChart={props}></div>// main.ts - register the elements once
import "@michi-vz/angular";
import { applyGapChartProps } from "@michi-vz/angular";
// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-gap-chart #c></michi-vz-gap-chart>
applyGapChartProps(this.c.nativeElement, props);<script type="module" src="https://cdn.jsdelivr.net/npm/@michi-vz/wc/dist/michi-vz-wc.bundle.js"></script>
<michi-vz-gap-chart id="c"></michi-vz-gap-chart>
<script>
Object.assign(document.getElementById("c"), props); // dataSet/series, title, …
</script>import { mountGapChart } from "@michi-vz/core";
const chart = mountGapChart(el, props);
chart.update(next);
chart.getContext(); // renderer-agnostic, LLM-ready
chart.destroy();API
Props are typed as GapChartProps in @michi-vz/core. Shared across all charts: width, height, margin, colors / colorsMapping, renderer ("svg", "canvas", or experimental "webgpu"), highlightItems, disabledItems, and the on* callbacks. onChartDataProcessed / getContext() return the renderer-agnostic ChartContext.
