ScatterPlot
Two-axis scatter with optional quadrants and bubble sizing.
A token-driven scatter plot for comparing entities on two metrics at once. Each datum is a dot positioned by `x`/`y`, optionally sized by `size` (sqrt-scaled so area is true) and tinted by `color`. Pass `xRef`/`yRef` to split the plane into quadrants and `quadrantLabels` to caption them — ideal for "high score vs low wins"-style comparisons. Axes use the padded data extent (not forced to zero), every point is keyboard-focusable with an accessible tooltip, and empty input shows an em-dash placeholder.
Demo
Usage
<script>
import { ScatterPlot } from 'dssoca';
const points = [
{ label: 'Ana', x: 8.9, y: 7.5, size: 27 },
{ label: 'Bruno', x: 8.5, y: 5.7, size: 14 },
{ label: 'Caio', x: 7.7, y: 4.5, size: 7 },
];
</script>
<ScatterPlot
{points}
xRef={8}
yRef={5}
xLabel="Consistency"
yLabel="Power rating"
quadrantLabels={{ tr: 'steady star', br: 'spiky' }}
/>HTML
CSS only — static markup The exact markup the component renders, generated at build time. Use it with dssoca/vanilla.css — see Plain HTML & CSS.
<div class="ss-scatter" role="group" aria-label="Scatter plot with 3 points (api, db, cache).">
<div class="plot">
<svg class="canvas" viewBox="0 0 420 320" width="420" height="320" preserveAspectRatio="xMidYMid meet" role="presentation">
<g transform="translate(48,12)">
<line class="grid" x1="0" x2="356" y1="272" y2="272">
</line>
<text class="tick y" x="-6" y="272" dy="0.32em" text-anchor="end">90</text>
<line class="grid" x1="0" x2="356" y1="217.60000000000002" y2="217.60000000000002">
</line>
<text class="tick y" x="-6" y="217.60000000000002" dy="0.32em" text-anchor="end">92</text>
<line class="grid" x1="0" x2="356" y1="163.2" y2="163.2">
</line>
<text class="tick y" x="-6" y="163.2" dy="0.32em" text-anchor="end">94</text>
<line class="grid" x1="0" x2="356" y1="108.80000000000001" y2="108.80000000000001">
</line>
<text class="tick y" x="-6" y="108.80000000000001" dy="0.32em" text-anchor="end">96</text>
<line class="grid" x1="0" x2="356" y1="54.39999999999999" y2="54.39999999999999">
</line>
<text class="tick y" x="-6" y="54.39999999999999" dy="0.32em" text-anchor="end">98</text>
<line class="grid" x1="0" x2="356" y1="0" y2="0">
</line>
<text class="tick y" x="-6" y="0" dy="0.32em" text-anchor="end">100</text>
<text class="tick x" x="0" y="288" text-anchor="middle">0</text>
<text class="tick x" x="79.1111111111111" y="288" text-anchor="middle">10</text>
<text class="tick x" x="158.2222222222222" y="288" text-anchor="middle">20</text>
<text class="tick x" x="237.33333333333331" y="288" text-anchor="middle">30</text>
<text class="tick x" x="316.4444444444444" y="288" text-anchor="middle">40</text>
<line class="axis" x1="0" x2="0" y1="0" y2="272">
</line>
<line class="axis" x1="0" x2="356" y1="272" y2="272">
</line>
<circle class="point" cx="94.93333333333334" cy="54.39999999999999" r="5" style="fill:var(--ss-accent)" tabindex="0" role="button" aria-label="api: 12, 98">
</circle>
<text class="point-label" x="103.93333333333334" y="54.39999999999999" dy="0.32em">api</text>
<circle class="point" cx="316.4444444444444" cy="244.8" r="5" style="fill:var(--ss-blue)" tabindex="0" role="button" aria-label="db: 40, 91">
</circle>
<text class="point-label" x="325.4444444444444" y="244.8" dy="0.32em">db</text>
<circle class="point" cx="23.733333333333334" cy="27.199999999999996" r="5" style="fill:var(--ss-magenta)" tabindex="0" role="button" aria-label="cache: 3, 99">
</circle>
<text class="point-label" x="32.733333333333334" y="27.199999999999996" dy="0.32em">cache</text>
<text class="axis-label x" x="178" y="304" text-anchor="middle">latency ms</text>
<text class="axis-label y" transform="translate(-36,136) rotate(-90)" text-anchor="middle">uptime %</text>
</g>
</svg>
</div>
</div>Rendered geometry: copy as-is. Hover tooltips are not wired by vanilla.js.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
points | ScatterPoint[] | — | Required. Each point has a `label`, `x`, `y`, and optional `size` (bubble area) and `color`. |
xLabel | string | — | X axis caption drawn under the axis. |
yLabel | string | — | Y axis caption drawn rotated beside the axis. |
xRef | number | — | Dashed vertical reference line at this x (e.g. the median) — splits into quadrants. |
yRef | number | — | Dashed horizontal reference line at this y — splits into quadrants. |
quadrantLabels | { tl?; tr?; bl?; br? } | — | Corner captions for the four quadrants (reads best with xRef + yRef). |
xDomain | [number, number] | — | Force the x domain; defaults to the padded data extent. |
yDomain | [number, number] | — | Force the y domain; defaults to the padded data extent. |
xFormat | (x: number) => string | — | Format an x value for ticks + tooltip. |
yFormat | (y: number) => string | — | Format a y value for ticks + tooltip. |
showLabels | boolean | true | Draw each point's label next to its dot. |
height | number | 320 | Intrinsic drawing height in px. |
width | number | 420 | Intrinsic drawing width in px. |
fluid | boolean | false | Stretch to the container width via the viewBox. |
tooltip | boolean | true | Reveal a tooltip on hover/focus of a point. Each point is keyboard-focusable. |
summary | string | — | Screen-reader summary + accessible name; auto-generated when absent. |
size | 'sm' | 'md' | 'lg' | — | Per-instance size override; inherits the ancestor `data-size-variant` when unset. |