Bubble Chart
Composition
"How big is each one, and how much of it is already realized?" A bubble cloud answers magnitude at a glance: every circle is sized by value (area, not radius), and a gravity simulation pulls them into a tidy cluster so the big ones obviously dominate. Like the treemap, each bubble can carry a two-part split - a solid realized core inside a lighter untapped ring - so you read size and progress together.
No split needed? Drop partial for a clean proportional cloud, one colour per category:
The cluster is laid out with d3-force: bubbles fall toward the centre (
gravity) and push apart so they never overlap (collision). The simulation is settled synchronously, so SVG and canvas render the identical, reproducible layout.
Usage
import { BubbleChart } from "@michi-vz/react";
export default () => <BubbleChart {...props} />; // props = the chart options<script setup>
import { BubbleChart } from "@michi-vz/vue";
</script>
<template>
<BubbleChart :options="props" />
</template><script>
import { bubbleChart } from "@michi-vz/svelte";
</script>
<div use:bubbleChart={props}></div>// main.ts - register the elements once
import "@michi-vz/angular";
import { applyBubbleChartProps } from "@michi-vz/angular";
// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-bubble-chart #c></michi-vz-bubble-chart>
applyBubbleChartProps(this.c.nativeElement, props);<script type="module" src="https://cdn.jsdelivr.net/npm/@michi-vz/wc"></script>
<michi-vz-bubble-chart id="c"></michi-vz-bubble-chart>
<script>
Object.assign(document.getElementById("c"), props); // dataSet, splitLabels, …
</script>import { mountBubbleChart } from "@michi-vz/core";
const chart = mountBubbleChart(el, props);
chart.update(next);
chart.getContext(); // renderer-agnostic, LLM-ready
chart.destroy();Data shape
Each dataSet item is one bubble: a label, a value (area), an optional partial (the realized sub-portion), and an optional color.
const props = {
splitLabels: ["Realized", "Untapped"],
showLegend: true,
gravity: 0.09, // higher = tighter cluster
dataSet: [
{ label: "Germany", value: 120, partial: 64 }, // 53% realized
{ label: "United States", value: 152, partial: 88 },
{ label: "China", value: 168, partial: 51 },
],
};Gravity & the split
gravity sets how strongly bubbles are pulled toward the centre (higher = tighter), padding the gap between them, and fillRatio how much of the plot the cloud fills. The split mirrors the treemap: partial carves an area-true realized core (radius r·√(partial/value)), and the rest reads as a lighter tint of the same hue - a solid colour under a white veil, so it works on light and dark backgrounds. Name the parts with splitLabels.
API
Props are typed as BubbleChartProps in @michi-vz/core. Shared across all charts: width, height, margin, colors / colorsMapping, renderer ("svg" | "canvas"), highlightItems, disabledItems, and the on* callbacks. onChartDataProcessed / getContext() return the renderer-agnostic ChartContext. Full reference: Bubble API.