BoxPlot
Box-and-whisker distributions with an optional point overlay.
A token-driven box plot for comparing the score distributions of a few players (or any groups) at a glance — a short box means a consistent scorer. Each group draws Tukey whiskers, a quartile box (Q1–Q3) tinted in the group `color`, and a median line; pass `showPoints` to overlay every value as a deterministically jittered dot (no SSR/hydration randomness). The y axis uses the padded data extent across all values, every box is keyboard-focusable with a five-number-summary tooltip, and empty input shows an em-dash placeholder.
Demo
Usage
<script>
import { BoxPlot } from 'dssoca';
const groups = [
{ label: 'Ana', values: [20000, 21000, 19500, 22000, 20500] },
{ label: 'Bruno', values: [15000, 18000, 12000, 22000, 9000] },
{ label: 'Caio', values: [17000, 17500, 16800, 18200, 17000] },
];
</script>
<BoxPlot {groups} yLabel="Score" />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-boxplot" role="group" aria-label="Box plot with 2 groups (api, db).">
<div class="plot">
<svg class="canvas" viewBox="0 0 420 300" width="420" height="300" preserveAspectRatio="xMidYMid meet" role="presentation">
<g transform="translate(48,12)">
<line class="grid" x1="0" x2="356" y1="220.5" y2="220.5">
</line>
<text class="tick y" x="-6" y="220.5" dy="0.32em" text-anchor="end">10</text>
<line class="grid" x1="0" x2="356" y1="157.5" y2="157.5">
</line>
<text class="tick y" x="-6" y="157.5" dy="0.32em" text-anchor="end">20</text>
<line class="grid" x1="0" x2="356" y1="94.5" y2="94.5">
</line>
<text class="tick y" x="-6" y="94.5" dy="0.32em" text-anchor="end">30</text>
<line class="grid" x1="0" x2="356" y1="31.5" y2="31.5">
</line>
<text class="tick y" x="-6" y="31.5" dy="0.32em" text-anchor="end">40</text>
<line class="axis" x1="0" x2="0" y1="0" y2="252">
</line>
<line class="axis" x1="0" x2="356" y1="252" y2="252">
</line>
<line class="whisker" x1="100.60869565217392" x2="100.60869565217392" y1="207.89999999999998" y2="144.89999999999998">
</line>
<line class="whisker" x1="73.52173913043478" x2="127.69565217391306" y1="207.89999999999998" y2="207.89999999999998">
</line>
<line class="whisker" x1="73.52173913043478" x2="127.69565217391306" y1="144.89999999999998" y2="144.89999999999998">
</line>
<rect class="box" x="46.434782608695656" y="166.95" width="108.34782608695653" height="26.775000000000034" style="fill:var(--ss-accent);stroke:var(--ss-accent)" tabindex="0" role="button" aria-label="api: median 16 (Q1 14.25–Q3 18.5)">
</rect>
<line class="median" x1="46.434782608695656" x2="154.7826086956522" y1="182.7" y2="182.7" style="stroke:var(--ss-accent)">
</line>
<circle class="point" cx="62.68695652173914" cy="207.89999999999998" r="2.5" style="fill:var(--ss-accent)" aria-hidden="true">
</circle>
<circle class="point" cx="87.3824597110958" cy="189" r="2.5" style="fill:var(--ss-accent)" aria-hidden="true">
</circle>
<circle class="point" cx="124.04734113706093" cy="195.3" r="2.5" style="fill:var(--ss-accent)" aria-hidden="true">
</circle>
<circle class="point" cx="117.76274490769683" cy="163.8" r="2.5" style="fill:var(--ss-accent)" aria-hidden="true">
</circle>
<circle class="point" cx="130.54525904290912" cy="144.89999999999998" r="2.5" style="fill:var(--ss-accent)" aria-hidden="true">
</circle>
<circle class="point" cx="102.00773980555583" cy="176.39999999999998" r="2.5" style="fill:var(--ss-accent)" aria-hidden="true">
</circle>
<line class="whisker" x1="255.39130434782612" x2="255.39130434782612" y1="107.10000000000001" y2="31.5">
</line>
<line class="whisker" x1="228.304347826087" x2="282.47826086956525" y1="107.10000000000001" y2="107.10000000000001">
</line>
<line class="whisker" x1="228.304347826087" x2="282.47826086956525" y1="31.5" y2="31.5">
</line>
<rect class="box" x="201.21739130434784" y="66.14999999999999" width="108.34782608695653" height="26.77500000000002" style="fill:var(--ss-blue);stroke:var(--ss-blue)" tabindex="0" role="button" aria-label="db: median 32 (Q1 30.25–Q3 34.5)">
</rect>
<line class="median" x1="201.21739130434784" x2="309.5652173913044" y1="81.89999999999999" y2="81.89999999999999" style="stroke:var(--ss-blue)">
</line>
<circle class="point" cx="217.46956521739133" cy="94.5" r="2.5" style="fill:var(--ss-blue)" aria-hidden="true">
</circle>
<circle class="point" cx="242.165068406748" cy="107.10000000000001" r="2.5" style="fill:var(--ss-blue)" aria-hidden="true">
</circle>
<circle class="point" cx="278.8299498327131" cy="63" r="2.5" style="fill:var(--ss-blue)" aria-hidden="true">
</circle>
<circle class="point" cx="272.54535360334904" cy="31.5" r="2.5" style="fill:var(--ss-blue)" aria-hidden="true">
</circle>
<circle class="point" cx="285.32786773856134" cy="75.60000000000001" r="2.5" style="fill:var(--ss-blue)" aria-hidden="true">
</circle>
<circle class="point" cx="256.79034850120803" cy="88.19999999999999" r="2.5" style="fill:var(--ss-blue)" aria-hidden="true">
</circle>
<text class="tick x" x="100.60869565217392" y="268" text-anchor="middle">api</text>
<text class="tick x" x="255.39130434782612" y="268" text-anchor="middle">db</text>
<text class="axis-label y" transform="translate(-36,126) rotate(-90)" text-anchor="middle">ms</text>
</g>
</svg>
</div>
</div>Charts are rendered geometry: copy the SVG as-is or render it with the Svelte component. Hover tooltips are not wired by vanilla.js.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
groups | BoxGroup[] | — | Required. One box per group; each has a `label`, `values` (number[]), and optional `color`. |
showPoints | boolean | true | Overlay every value as a small jittered dot (deterministic, SSR-safe). |
yLabel | string | — | Y axis caption drawn rotated beside the axis. |
yDomain | [number, number] | — | Force the y domain; defaults to the padded data extent across all values. |
yFormat | (y: number) => string | — | Format a y value for ticks + tooltip. |
height | number | 300 | 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 five-number-summary tooltip on hover/focus of a box. Each box 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. |