Scatter plot using bubble size as another dimension.
<figure class="bubble-chart-demo bubble-chart-demo--emphasis-medium bubble-chart-demo--count-5" style="--accent:#D9C8F0;">
<figcaption class="bubble-chart-demo__title">Deal value by product</figcaption>
<svg class="bubble-chart-demo__svg" viewBox="0 0 300 150" role="img" aria-label="Bubble chart plotting product engagement against reach, bubble size shows deal value, featured product sized medium">
<g aria-hidden="true">
<line x1="30" y1="10" x2="30" y2="130" class="bubble-chart-demo__axisline" />
<line x1="30" y1="130" x2="300" y2="130" class="bubble-chart-demo__axisline" />
<text x="26" y="18" class="bubble-chart-demo__tick" text-anchor="end">40k</text>
<text x="26" y="74" class="bubble-chart-demo__tick" text-anchor="end">20k</text>
<text x="26" y="130" class="bubble-chart-demo__tick" text-anchor="end">0k</text>
<text x="165" y="146" class="bubble-chart-demo__axislabel" text-anchor="middle">Engagement (visits, k)</text>
</g>
<g class="bubble-chart-demo__bubble bubble-chart-demo__bubble--b">
<circle cx="120" cy="60" r="14" class="bubble-chart-demo__circle" />
<text x="120" y="40" class="bubble-chart-demo__value">$14k</text>
</g>
<g class="bubble-chart-demo__bubble bubble-chart-demo__bubble--c">
<circle cx="180" cy="100" r="10" class="bubble-chart-demo__circle" />
<text x="180" y="84" class="bubble-chart-demo__value">$10k</text>
</g>
<g class="bubble-chart-demo__bubble bubble-chart-demo__bubble--d">
<circle cx="230" cy="50" r="18" class="bubble-chart-demo__circle" />
<text x="230" y="26" class="bubble-chart-demo__value">$18k</text>
</g>
<g class="bubble-chart-demo__bubble bubble-chart-demo__bubble--e">
<circle cx="270" cy="120" r="8" class="bubble-chart-demo__circle" />
<text x="270" y="106" class="bubble-chart-demo__value">$8k</text>
</g>
<g class="bubble-chart-demo__bubble bubble-chart-demo__bubble--a-small">
<circle cx="60" cy="110" r="8" class="bubble-chart-demo__circle bubble-chart-demo__circle--featured" />
<text x="60" y="96" class="bubble-chart-demo__value">$20k</text>
</g>
<g class="bubble-chart-demo__bubble bubble-chart-demo__bubble--a-medium">
<circle cx="60" cy="110" r="16" class="bubble-chart-demo__circle bubble-chart-demo__circle--featured" />
<text x="60" y="88" class="bubble-chart-demo__value">$20k</text>
</g>
<g class="bubble-chart-demo__bubble bubble-chart-demo__bubble--a-large">
<circle cx="60" cy="110" r="26" class="bubble-chart-demo__circle bubble-chart-demo__circle--featured" />
<text x="60" y="78" class="bubble-chart-demo__value">$20k</text>
</g>
</svg>
<div class="bubble-chart-demo__legend">
<span class="bubble-chart-demo__legend-item"><svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true"><circle cx="5" cy="5" r="3" class="bubble-chart-demo__legend-dot" /></svg>Low deal value</span>
<span class="bubble-chart-demo__legend-item"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6" class="bubble-chart-demo__legend-dot" /></svg>Medium deal value</span>
<span class="bubble-chart-demo__legend-item"><svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true"><circle cx="11" cy="11" r="9" class="bubble-chart-demo__legend-dot" /></svg>High deal value</span>
</div>
</figure>
<style>
.bubble-chart-demo { font: 12px system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 16px; border: 1.5px solid #24143022; border-radius: 12px; box-sizing: border-box; max-width: 640px; }
.bubble-chart-demo__title { font-size: 13px; font-weight: 600; margin: 0 0 10px; }
.bubble-chart-demo__svg { width: 100%; display: block; overflow: visible; }
.bubble-chart-demo__axisline { stroke: #24143055; stroke-width: 1; }
.bubble-chart-demo__tick { font-size: 9px; fill: #24143099; }
.bubble-chart-demo__axislabel { font-size: 9px; fill: #24143099; }
.bubble-chart-demo__circle { fill: var(--accent); fill-opacity: .55; stroke: var(--accent); stroke-width: 1.5; }
.bubble-chart-demo__value { font-size: 9px; fill: #241430; text-anchor: middle; }
.bubble-chart-demo__bubble--a-small, .bubble-chart-demo__bubble--a-medium, .bubble-chart-demo__bubble--a-large { display: none; }
.bubble-chart-demo--emphasis-small .bubble-chart-demo__bubble--a-small,
.bubble-chart-demo--emphasis-medium .bubble-chart-demo__bubble--a-medium,
.bubble-chart-demo--emphasis-large .bubble-chart-demo__bubble--a-large { display: block; }
.bubble-chart-demo--count-3 .bubble-chart-demo__bubble--d,
.bubble-chart-demo--count-3 .bubble-chart-demo__bubble--e,
.bubble-chart-demo--count-4 .bubble-chart-demo__bubble--e { display: none; }
.bubble-chart-demo__legend { display: flex; gap: 14px; margin-top: 10px; flex-wrap: wrap; }
.bubble-chart-demo__legend-item { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; }
.bubble-chart-demo__legend-dot { fill: var(--accent); fill-opacity: .55; stroke: var(--accent); stroke-width: 1.5; }
</style><figure class="bubble-chart-tw bubble-chart-tw--emphasis-medium bubble-chart-tw--count-5 max-w-[640px] p-4 border-[1.5px] border-[#24143022] rounded-xl box-border font-sans text-[#241430] bg-[#FAF7F2]" style="--accent:#D9C8F0;">
<figcaption class="text-[13px] font-semibold mb-2.5">Deal value by product</figcaption>
<svg class="w-full block" style="overflow:visible;" viewBox="0 0 300 150" role="img" aria-label="Bubble chart plotting product engagement against reach, bubble size shows deal value, featured product sized medium">
<g aria-hidden="true">
<line x1="30" y1="10" x2="30" y2="130" stroke="#24143055" />
<line x1="30" y1="130" x2="300" y2="130" stroke="#24143055" />
<text x="26" y="18" font-size="9" fill="#24143099" text-anchor="end">40k</text>
<text x="26" y="74" font-size="9" fill="#24143099" text-anchor="end">20k</text>
<text x="26" y="130" font-size="9" fill="#24143099" text-anchor="end">0k</text>
<text x="165" y="146" font-size="9" fill="#24143099" text-anchor="middle">Engagement (visits, k)</text>
</g>
<g class="bubble-chart-tw__bubble bubble-chart-tw__bubble--b">
<circle cx="120" cy="60" r="14" class="bubble-chart-tw__circle" />
<text x="120" y="40" class="bubble-chart-tw__value">$14k</text>
</g>
<g class="bubble-chart-tw__bubble bubble-chart-tw__bubble--c">
<circle cx="180" cy="100" r="10" class="bubble-chart-tw__circle" />
<text x="180" y="84" class="bubble-chart-tw__value">$10k</text>
</g>
<g class="bubble-chart-tw__bubble bubble-chart-tw__bubble--d">
<circle cx="230" cy="50" r="18" class="bubble-chart-tw__circle" />
<text x="230" y="26" class="bubble-chart-tw__value">$18k</text>
</g>
<g class="bubble-chart-tw__bubble bubble-chart-tw__bubble--e">
<circle cx="270" cy="120" r="8" class="bubble-chart-tw__circle" />
<text x="270" y="106" class="bubble-chart-tw__value">$8k</text>
</g>
<g class="bubble-chart-tw__bubble bubble-chart-tw__bubble--a-small">
<circle cx="60" cy="110" r="8" class="bubble-chart-tw__circle bubble-chart-tw__circle--featured" />
<text x="60" y="96" class="bubble-chart-tw__value">$20k</text>
</g>
<g class="bubble-chart-tw__bubble bubble-chart-tw__bubble--a-medium">
<circle cx="60" cy="110" r="16" class="bubble-chart-tw__circle bubble-chart-tw__circle--featured" />
<text x="60" y="88" class="bubble-chart-tw__value">$20k</text>
</g>
<g class="bubble-chart-tw__bubble bubble-chart-tw__bubble--a-large">
<circle cx="60" cy="110" r="26" class="bubble-chart-tw__circle bubble-chart-tw__circle--featured" />
<text x="60" y="78" class="bubble-chart-tw__value">$20k</text>
</g>
</svg>
<div class="flex gap-3.5 mt-2.5 flex-wrap">
<span class="inline-flex items-center gap-1 text-[11px]"><svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true"><circle cx="5" cy="5" r="3" style="fill:#D9C8F0;fill-opacity:.55;stroke:#D9C8F0;stroke-width:1.5;" /></svg>Low deal value</span>
<span class="inline-flex items-center gap-1 text-[11px]"><svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6" style="fill:#D9C8F0;fill-opacity:.55;stroke:#D9C8F0;stroke-width:1.5;" /></svg>Medium deal value</span>
<span class="inline-flex items-center gap-1 text-[11px]"><svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true"><circle cx="11" cy="11" r="9" style="fill:#D9C8F0;fill-opacity:.55;stroke:#D9C8F0;stroke-width:1.5;" /></svg>High deal value</span>
</div>
</figure>
<style>
.bubble-chart-tw__circle, [class*="bubble-chart-tw__circle"] { fill: var(--accent); fill-opacity: .55; stroke: var(--accent); stroke-width: 1.5; }
.bubble-chart-tw__value { font-size: 9px; fill: #241430; text-anchor: middle; }
.bubble-chart-tw__bubble--a-small, .bubble-chart-tw__bubble--a-medium, .bubble-chart-tw__bubble--a-large { display: none; }
.bubble-chart-tw--emphasis-small .bubble-chart-tw__bubble--a-small,
.bubble-chart-tw--emphasis-medium .bubble-chart-tw__bubble--a-medium,
.bubble-chart-tw--emphasis-large .bubble-chart-tw__bubble--a-large { display: block; }
.bubble-chart-tw--count-3 .bubble-chart-tw__bubble--d,
.bubble-chart-tw--count-3 .bubble-chart-tw__bubble--e,
.bubble-chart-tw--count-4 .bubble-chart-tw__bubble--e { display: none; }
</style>export function BubbleChart({
emphasis = "medium",
accent = "#D9C8F0",
showLabels = true,
bubbleCount = "5",
}) {
const emphasisRadii = { small: 8, medium: 16, large: 26 };
const aR = emphasisRadii[emphasis] ?? 16;
const aX = 60, aY = 110;
const rest = [
{ id: "b", x: 120, y: 60, r: 14, val: "$14k", labelY: 40 },
{ id: "c", x: 180, y: 100, r: 10, val: "$10k", labelY: 84 },
{ id: "d", x: 230, y: 50, r: 18, val: "$18k", labelY: 26 },
{ id: "e", x: 270, y: 120, r: 8, val: "$8k", labelY: 106 },
].slice(0, Number(bubbleCount) - 1);
const circleStyle = { fill: accent, fillOpacity: 0.55, stroke: accent, strokeWidth: 1.5 };
return (
<figure style={{ font: "12px system-ui, sans-serif", color: "#241430", background: "#FAF7F2", padding: 16, border: "1.5px solid #24143022", borderRadius: 12, boxSizing: "border-box", maxWidth: 640 }}>
<figcaption style={{ fontSize: 13, fontWeight: 600, marginBottom: 10 }}>Deal value by product</figcaption>
<svg viewBox="0 0 300 150" style={{ width: "100%", display: "block", overflow: "visible" }} role="img" aria-label={"Bubble chart plotting product engagement against reach, bubble size shows deal value, featured product sized " + emphasis}>
<g aria-hidden="true">
<line x1="30" y1="10" x2="30" y2="130" stroke="#24143055" />
<line x1="30" y1="130" x2="300" y2="130" stroke="#24143055" />
<text x="26" y="18" fontSize="9" fill="#24143099" textAnchor="end">40k</text>
<text x="26" y="74" fontSize="9" fill="#24143099" textAnchor="end">20k</text>
<text x="26" y="130" fontSize="9" fill="#24143099" textAnchor="end">0k</text>
<text x="165" y="146" fontSize="9" fill="#24143099" textAnchor="middle">Engagement (visits, k)</text>
</g>
{rest.map((b) => (
<g key={b.id}>
<circle cx={b.x} cy={b.y} r={b.r} style={circleStyle} />
{showLabels && <text x={b.x} y={b.labelY} fontSize="9" fill="#241430" textAnchor="middle">{b.val}</text>}
</g>
))}
<g>
<circle cx={aX} cy={aY} r={aR} style={circleStyle} />
{showLabels && <text x={aX} y={aY - aR - 6} fontSize="9" fill="#241430" textAnchor="middle">$20k</text>}
</g>
</svg>
<div style={{ display: "flex", gap: 14, marginTop: 10, flexWrap: "wrap" }}>
<span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11 }}>
<svg width="10" height="10" viewBox="0 0 10 10" aria-hidden="true"><circle cx="5" cy="5" r="3" style={circleStyle} /></svg>Low deal value
</span>
<span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11 }}>
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true"><circle cx="8" cy="8" r="6" style={circleStyle} /></svg>Medium deal value
</span>
<span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11 }}>
<svg width="22" height="22" viewBox="0 0 22 22" aria-hidden="true"><circle cx="11" cy="11" r="9" style={circleStyle} /></svg>High deal value
</span>
</div>
</figure>
);
}