Scatter Plot

Individual data points showing relationships.

html

<figure class="scatter-plot scatter-plot--positive" style="--accent:#F5C2E0;--r:3.5;" role="img" aria-label="Scatter plot showing a positive correlation between score X and score Y across 8 points">
  <svg class="scatter-plot__svg" viewBox="0 0 140 122" xmlns="http://www.w3.org/2000/svg">
    <g aria-hidden="true">
      <line class="scatter-plot__grid" x1="20" y1="10" x2="20" y2="100"></line>
      <line class="scatter-plot__grid" x1="20" y1="100" x2="130" y2="100"></line>
      <text class="scatter-plot__tick" x="16" y="103">0</text>
      <text class="scatter-plot__tick" x="16" y="58">50</text>
      <text class="scatter-plot__tick" x="16" y="14">100</text>
      <text class="scatter-plot__tick" x="20" y="112">0</text>
      <text class="scatter-plot__tick" x="75" y="112">50</text>
      <text class="scatter-plot__tick" x="127" y="112">100</text>
      <text class="scatter-plot__axistitle" x="6" y="8">Y (pts)</text>
      <text class="scatter-plot__axistitle" x="75" y="119">X (pts)</text></g>
    <g class="scatter-plot__series scatter-plot__series--positive"><circle class="sp-point scatter-plot__point--positive" cx="30" cy="88"></circle>
      <circle class="sp-point scatter-plot__point--positive" cx="45" cy="78"></circle>
      <circle class="sp-point scatter-plot__point--positive" cx="55" cy="70"></circle>
      <circle class="sp-point scatter-plot__point--positive" cx="68" cy="64"></circle>
      <circle class="sp-point scatter-plot__point--positive" cx="80" cy="52"></circle>
      <circle class="sp-point scatter-plot__point--positive" cx="92" cy="44"></circle>
      <circle class="sp-point scatter-plot__point--positive" cx="105" cy="32"></circle>
      <circle class="sp-point scatter-plot__point--positive" cx="118" cy="22"></circle>
      <polyline class="scatter-plot__trend" points="28,90 120,20"></polyline>
    </g>
    <g class="scatter-plot__series scatter-plot__series--negative"><circle class="sp-point scatter-plot__point--negative" cx="30" cy="22"></circle>
      <circle class="sp-point scatter-plot__point--negative" cx="45" cy="30"></circle>
      <circle class="sp-point scatter-plot__point--negative" cx="55" cy="40"></circle>
      <circle class="sp-point scatter-plot__point--negative" cx="68" cy="48"></circle>
      <circle class="sp-point scatter-plot__point--negative" cx="80" cy="58"></circle>
      <circle class="sp-point scatter-plot__point--negative" cx="92" cy="66"></circle>
      <circle class="sp-point scatter-plot__point--negative" cx="105" cy="76"></circle>
      <circle class="sp-point scatter-plot__point--negative" cx="118" cy="86"></circle>
      <polyline class="scatter-plot__trend" points="28,20 120,90"></polyline>
    </g>
    <g class="scatter-plot__series scatter-plot__series--none"><circle class="sp-point scatter-plot__point--none" cx="30" cy="50"></circle>
      <circle class="sp-point scatter-plot__point--none" cx="45" cy="75"></circle>
      <circle class="sp-point scatter-plot__point--none" cx="55" cy="30"></circle>
      <circle class="sp-point scatter-plot__point--none" cx="68" cy="60"></circle>
      <circle class="sp-point scatter-plot__point--none" cx="80" cy="40"></circle>
      <circle class="sp-point scatter-plot__point--none" cx="92" cy="80"></circle>
      <circle class="sp-point scatter-plot__point--none" cx="105" cy="35"></circle>
      <circle class="sp-point scatter-plot__point--none" cx="118" cy="55"></circle>
      <polyline class="scatter-plot__trend" points="28,55 120,58"></polyline>
    </g>
  </svg>
</figure>
<style>
.scatter-plot { max-width: 380px; font: 11px system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 14px; box-sizing: border-box; }
.scatter-plot__svg { width: 100%; height: auto; display: block; overflow: visible; }
.scatter-plot__grid { stroke: #24143055; stroke-width: 1; }
.scatter-plot__tick { font-size: 7.5px; fill: #24143099; text-anchor: end; }
.scatter-plot__axistitle { font-size: 7.5px; fill: #24143099; text-anchor: middle; }
.scatter-plot__series { display: none; }
.scatter-plot--positive .scatter-plot__series--positive { display: block; }
.scatter-plot--negative .scatter-plot__series--negative { display: block; }
.scatter-plot--none .scatter-plot__series--none { display: block; }
.scatter-plot__point { fill: var(--accent); stroke: #241430; stroke-width: .75; r: var(--r); }
.scatter-plot__trend { fill: none; stroke: #241430; stroke-width: 1.5; stroke-dasharray: 4 3; }
</style>

tailwind

<figure class="scatter-plot-tw scatter-plot-tw--positive" style="--accent:#F5C2E0;--r:3.5;" role="img" aria-label="Scatter plot showing a positive correlation between score X and score Y across 8 points">
  <svg class="scatter-plot-tw__svg" viewBox="0 0 140 122" xmlns="http://www.w3.org/2000/svg">
    <g aria-hidden="true">
      <line class="scatter-plot-tw__grid" x1="20" y1="10" x2="20" y2="100"></line>
      <line class="scatter-plot-tw__grid" x1="20" y1="100" x2="130" y2="100"></line>
      <text class="scatter-plot-tw__tick" x="16" y="103">0</text>
      <text class="scatter-plot-tw__tick" x="16" y="58">50</text>
      <text class="scatter-plot-tw__tick" x="16" y="14">100</text>
      <text class="scatter-plot-tw__tick" x="20" y="112">0</text>
      <text class="scatter-plot-tw__tick" x="75" y="112">50</text>
      <text class="scatter-plot-tw__tick" x="127" y="112">100</text>
      <text class="scatter-plot-tw__axistitle" x="6" y="8">Y (pts)</text>
      <text class="scatter-plot-tw__axistitle" x="75" y="119">X (pts)</text></g>
    <g class="scatter-plot-tw__series scatter-plot-tw__series--positive"><circle class="sp-point scatter-plot-tw__point--positive" cx="30" cy="88"></circle>
      <circle class="sp-point scatter-plot-tw__point--positive" cx="45" cy="78"></circle>
      <circle class="sp-point scatter-plot-tw__point--positive" cx="55" cy="70"></circle>
      <circle class="sp-point scatter-plot-tw__point--positive" cx="68" cy="64"></circle>
      <circle class="sp-point scatter-plot-tw__point--positive" cx="80" cy="52"></circle>
      <circle class="sp-point scatter-plot-tw__point--positive" cx="92" cy="44"></circle>
      <circle class="sp-point scatter-plot-tw__point--positive" cx="105" cy="32"></circle>
      <circle class="sp-point scatter-plot-tw__point--positive" cx="118" cy="22"></circle>
      <polyline class="scatter-plot-tw__trend" points="28,90 120,20"></polyline>
    </g>
    <g class="scatter-plot-tw__series scatter-plot-tw__series--negative"><circle class="sp-point scatter-plot-tw__point--negative" cx="30" cy="22"></circle>
      <circle class="sp-point scatter-plot-tw__point--negative" cx="45" cy="30"></circle>
      <circle class="sp-point scatter-plot-tw__point--negative" cx="55" cy="40"></circle>
      <circle class="sp-point scatter-plot-tw__point--negative" cx="68" cy="48"></circle>
      <circle class="sp-point scatter-plot-tw__point--negative" cx="80" cy="58"></circle>
      <circle class="sp-point scatter-plot-tw__point--negative" cx="92" cy="66"></circle>
      <circle class="sp-point scatter-plot-tw__point--negative" cx="105" cy="76"></circle>
      <circle class="sp-point scatter-plot-tw__point--negative" cx="118" cy="86"></circle>
      <polyline class="scatter-plot-tw__trend" points="28,20 120,90"></polyline>
    </g>
    <g class="scatter-plot-tw__series scatter-plot-tw__series--none"><circle class="sp-point scatter-plot-tw__point--none" cx="30" cy="50"></circle>
      <circle class="sp-point scatter-plot-tw__point--none" cx="45" cy="75"></circle>
      <circle class="sp-point scatter-plot-tw__point--none" cx="55" cy="30"></circle>
      <circle class="sp-point scatter-plot-tw__point--none" cx="68" cy="60"></circle>
      <circle class="sp-point scatter-plot-tw__point--none" cx="80" cy="40"></circle>
      <circle class="sp-point scatter-plot-tw__point--none" cx="92" cy="80"></circle>
      <circle class="sp-point scatter-plot-tw__point--none" cx="105" cy="35"></circle>
      <circle class="sp-point scatter-plot-tw__point--none" cx="118" cy="55"></circle>
      <polyline class="scatter-plot-tw__trend" points="28,55 120,58"></polyline>
    </g>
  </svg>
</figure>
<style>
.scatter-plot-tw { max-width: 380px; font: 11px system-ui, sans-serif; color: #241430; background: #FAF7F2; padding: 14px; box-sizing: border-box; }
.scatter-plot-tw__svg { width: 100%; height: auto; display: block; overflow: visible; }
.scatter-plot-tw__grid { stroke: #24143055; stroke-width: 1; }
.scatter-plot-tw__tick { font-size: 7.5px; fill: #24143099; text-anchor: end; }
.scatter-plot-tw__axistitle { font-size: 7.5px; fill: #24143099; text-anchor: middle; }
.scatter-plot-tw__series { display: none; }
.scatter-plot-tw--positive .scatter-plot-tw__series--positive { display: block; }
.scatter-plot-tw--negative .scatter-plot-tw__series--negative { display: block; }
.scatter-plot-tw--none .scatter-plot-tw__series--none { display: block; }
.scatter-plot-tw__point { fill: var(--accent); stroke: #241430; stroke-width: .75; r: var(--r); }
.scatter-plot-tw__trend { fill: none; stroke: #241430; stroke-width: 1.5; stroke-dasharray: 4 3; }
</style>

react

export function ScatterPlot({
  correlation = "positive",
  pointSize = 3.5,
  showTrendLine = true,
  accent = "#F5C2E0",
}) {
  const series = {
    positive: { pts: [[30,88],[45,78],[55,70],[68,64],[80,52],[92,44],[105,32],[118,22]], trend: "28,90 120,20" },
    negative: { pts: [[30,22],[45,30],[55,40],[68,48],[80,58],[92,66],[105,76],[118,86]], trend: "28,20 120,90" },
    none: { pts: [[30,50],[45,75],[55,30],[68,60],[80,40],[92,80],[105,35],[118,55]], trend: "28,55 120,58" },
  };
  const active = series[correlation] ?? series.positive;
  return (
    <figure role="img" aria-label={`Scatter plot showing a ${correlation} correlation between score X and score Y across 8 points`}
      style={{ maxWidth: 380, padding: 14, font: "11px system-ui, sans-serif", color: "#241430", background: "#FAF7F2", boxSizing: "border-box" }}>
      <svg viewBox="0 0 140 122" style={{ width: "100%", height: "auto", display: "block", overflow: "visible" }}>
        <g aria-hidden="true">
          <line x1={20} y1={10} x2={20} y2={100} stroke="#24143055" />
          <line x1={20} y1={100} x2={130} y2={100} stroke="#24143055" />
          <text x={16} y={103} fontSize={7.5} fill="#24143099" textAnchor="end">0</text>
          <text x={16} y={58} fontSize={7.5} fill="#24143099" textAnchor="end">50</text>
          <text x={16} y={14} fontSize={7.5} fill="#24143099" textAnchor="end">100</text>
          <text x={20} y={112} fontSize={7.5} fill="#24143099" textAnchor="end">0</text>
          <text x={75} y={112} fontSize={7.5} fill="#24143099" textAnchor="end">50</text>
          <text x={127} y={112} fontSize={7.5} fill="#24143099" textAnchor="end">100</text>
          <text x={6} y={8} fontSize={7.5} fill="#24143099" textAnchor="middle">Y (pts)</text>
          <text x={75} y={119} fontSize={7.5} fill="#24143099" textAnchor="middle">X (pts)</text>
        </g>
        {active.pts.map(([x, y], i) => (
          <circle key={i} cx={x} cy={y} r={pointSize} fill={accent} stroke="#241430" strokeWidth={0.75} />
        ))}
        {showTrendLine && (
          <polyline points={active.trend} fill="none" stroke="#241430" strokeWidth={1.5} strokeDasharray="4 3" />
        )}
      </svg>
    </figure>
  );
}