DitherCharts

Scatter chart

Updated

ScatterChart plots one mark per row: a solid dot, or with sizeKey a bubble, its tone adding up where marks overlap. Like every DitherCharts chart, it fills its container's width, animates in, moves a cursor with the arrow keys, and keeps a data table for screen readers.

Example

Scatter

Ad spend against revenue, a dot per campaign

Scatter

Install it:

npx dithercharts add scatter-chart

Then use it:

import { ScatterChart } from "@/components/dithercharts/scatter-chart"
import type { ChartConfig } from "@/components/dithercharts/chart-config"

const chartData = [
  { spend: 5, revenue: 48, segment: "enterprise" },
  { spend: 42, revenue: 171, segment: "smb" },
  { spend: 79, revenue: 245, segment: "smb" },
  { spend: 20, revenue: 80, segment: "enterprise" },
  { spend: 57, revenue: 201, segment: "smb" },
  { spend: 94, revenue: 312, segment: "smb" },
  { spend: 35, revenue: 139, segment: "enterprise" },
  { spend: 72, revenue: 217, segment: "smb" },
  { spend: 13, revenue: 87, segment: "smb" },
  { spend: 50, revenue: 220, segment: "enterprise" },
  { spend: 87, revenue: 251, segment: "smb" },
  { spend: 28, revenue: 105, segment: "smb" },
  { spend: 65, revenue: 288, segment: "enterprise" },
  { spend: 6, revenue: 44, segment: "smb" },
  { spend: 43, revenue: 127, segment: "smb" },
  { spend: 80, revenue: 327, segment: "enterprise" },
  { spend: 21, revenue: 110, segment: "smb" },
  { spend: 58, revenue: 178, segment: "smb" },
  { spend: 95, revenue: 355, segment: "enterprise" },
  { spend: 36, revenue: 149, segment: "smb" },
  { spend: 73, revenue: 247, segment: "smb" },
  { spend: 14, revenue: 59, segment: "enterprise" },
  { spend: 51, revenue: 166, segment: "smb" },
  { spend: 88, revenue: 300, segment: "smb" },
  { spend: 29, revenue: 135, segment: "enterprise" },
  { spend: 66, revenue: 191, segment: "smb" },
  { spend: 7, revenue: 55, segment: "smb" },
  { spend: 44, revenue: 211, segment: "enterprise" },
  { spend: 81, revenue: 246, segment: "smb" },
  { spend: 22, revenue: 72, segment: "smb" },
  { spend: 59, revenue: 258, segment: "enterprise" },
  { spend: 96, revenue: 314, segment: "smb" },
  { spend: 37, revenue: 113, segment: "smb" },
  { spend: 74, revenue: 286, segment: "enterprise" },
  { spend: 15, revenue: 95, segment: "smb" },
  { spend: 52, revenue: 180, segment: "smb" },
  { spend: 89, revenue: 326, segment: "enterprise" },
  { spend: 30, revenue: 116, segment: "smb" },
  { spend: 67, revenue: 241, segment: "smb" },
  { spend: 8, revenue: 51, segment: "enterprise" },
]

const chartConfig = {
  spend: { label: "Ad spend" },
  revenue: { label: "Revenue" },
  smb: { label: "SMB" },
  enterprise: { label: "Enterprise" },
} satisfies ChartConfig

export function ChartScatter() {
  return (
    <ScatterChart
      data={chartData}
      config={chartConfig}
      xKey="spend"
      yKey="revenue"
      label="Ad spend vs revenue"
    />
  )
}

Installing needs DitherCharts Pro. See pricing.

Ad spend vs revenue
pointAd spendRevenue
Point 1548
Point 242171
Point 379245
Point 42080
Point 557201
Point 694312
Point 735139
Point 872217
Point 91387
Point 1050220
Point 1187251
Point 1228105
Point 1365288
Point 14644
Point 1543127
Point 1680327
Point 1721110
Point 1858178
Point 1995355
Point 2036149
Point 2173247
Point 221459
Point 2351166
Point 2488300
Point 2529135
Point 2666191
Point 27755
Point 2844211
Point 2981246
Point 302272
Point 3159258
Point 3296314
Point 3337113
Point 3474286
Point 351595
Point 3652180
Point 3789326
Point 3830116
Point 3967241
Point 40851
import { ScatterChart } from "@/components/dithercharts/scatter-chart"
import type { ChartConfig } from "@/components/dithercharts/chart-config"

const chartData = [
  { spend: 5, revenue: 48, segment: "enterprise" },
  { spend: 42, revenue: 171, segment: "smb" },
  { spend: 79, revenue: 245, segment: "smb" },
  { spend: 20, revenue: 80, segment: "enterprise" },
  { spend: 57, revenue: 201, segment: "smb" },
  { spend: 94, revenue: 312, segment: "smb" },
  { spend: 35, revenue: 139, segment: "enterprise" },
  { spend: 72, revenue: 217, segment: "smb" },
  { spend: 13, revenue: 87, segment: "smb" },
  { spend: 50, revenue: 220, segment: "enterprise" },
  { spend: 87, revenue: 251, segment: "smb" },
  { spend: 28, revenue: 105, segment: "smb" },
  { spend: 65, revenue: 288, segment: "enterprise" },
  { spend: 6, revenue: 44, segment: "smb" },
  { spend: 43, revenue: 127, segment: "smb" },
  { spend: 80, revenue: 327, segment: "enterprise" },
  { spend: 21, revenue: 110, segment: "smb" },
  { spend: 58, revenue: 178, segment: "smb" },
  { spend: 95, revenue: 355, segment: "enterprise" },
  { spend: 36, revenue: 149, segment: "smb" },
  { spend: 73, revenue: 247, segment: "smb" },
  { spend: 14, revenue: 59, segment: "enterprise" },
  { spend: 51, revenue: 166, segment: "smb" },
  { spend: 88, revenue: 300, segment: "smb" },
  { spend: 29, revenue: 135, segment: "enterprise" },
  { spend: 66, revenue: 191, segment: "smb" },
  { spend: 7, revenue: 55, segment: "smb" },
  { spend: 44, revenue: 211, segment: "enterprise" },
  { spend: 81, revenue: 246, segment: "smb" },
  { spend: 22, revenue: 72, segment: "smb" },
  { spend: 59, revenue: 258, segment: "enterprise" },
  { spend: 96, revenue: 314, segment: "smb" },
  { spend: 37, revenue: 113, segment: "smb" },
  { spend: 74, revenue: 286, segment: "enterprise" },
  { spend: 15, revenue: 95, segment: "smb" },
  { spend: 52, revenue: 180, segment: "smb" },
  { spend: 89, revenue: 326, segment: "enterprise" },
  { spend: 30, revenue: 116, segment: "smb" },
  { spend: 67, revenue: 241, segment: "smb" },
  { spend: 8, revenue: 51, segment: "enterprise" },
]

const chartConfig = {
  spend: { label: "Ad spend" },
  revenue: { label: "Revenue" },
  smb: { label: "SMB" },
  enterprise: { label: "Enterprise" },
} satisfies ChartConfig

export function ChartScatter() {
  return (
    <ScatterChart
      data={chartData}
      config={chartConfig}
      xKey="spend"
      yKey="revenue"
      label="Ad spend vs revenue"
    />
  )
}

Install

Charts install with DitherCharts Pro. See Installation to log in and import the stylesheet.

npx dithercharts add scatter-chart

Props

ScatterChart props
PropTypeDefaultDescription
datarequiredType readonly ChartRow[]Default –One row per point, e.g. { name: "Search", spend: 20, revenue: 90, deals: 8, segment: "ent" }; a row without a numeric x and y (or, with seriesKey, without a series value) is skipped.
configrequiredType ChartConfigDefault –Labels for xKey, yKey and sizeKey (the tooltip, the table and the live region), and with seriesKey one entry per series value: its label and pattern ("stripes" draws ring dots, or striped bubbles), in legend order. A series value must not also be an axis key.
xKeyrequiredType stringDefault –The row key that holds each point's x value, e.g. "spend".
yKeyrequiredType stringDefault –The row key that holds each point's y value, e.g. "revenue".
sizeKeyType stringDefault none (dots)The row key whose value sizes each point: a bubble chart, the bubble's area following the value.
seriesKeyType stringDefault none (one series)The row key whose value names each point's series, e.g. "segment".
nameKeyType stringDefault –The row key that names each point in the tooltip and the table, e.g. "name".
labelrequiredType stringDefault –Accessible name, e.g. "Spend vs revenue". Also captions the table twin.
legendType booleanDefault falseA legend under the chart whose buttons show and hide series (only with seriesKey).
yAxisType booleanDefault trueValue labels beside the grid lines.
densityType booleanDefault falseA faint halo round every dot, adding up to a darker cloud where points crowd (dots only).
tooltipType TooltipOptionsDefault –The tooltip's label, and the point (its row in data) it opens on at rest.
styleType StyleIdDefault "bayer4"Dither style.
animationType "develop" | "none"Default "develop"Entrance animation; reduced motion always shows the final frame.
heightType numberDefault 300Height in CSS pixels; the width fills the container.
cellType numberDefault 3Dither cell size in CSS pixels; a dot is 3 cells square.
classNameType stringDefault –Classes on the chart's outer figure.

Styles

The dither styles style takes:

  • "bayer2"
  • "bayer4"
  • "bayer8"
  • "blue-noise"
  • "halftone"
  • "crosshatch"
  • "pixel"
  • "stipple"
  • "engraved"
  • "mezzotint"
  • "woodcut"
  • "floyd-steinberg"
  • "atkinson"
  • "riemersma"

Keyboard and accessibility

The chart is one Tab stop. Focusing it from the keyboard puts the cursor on the first point, or where the keys last left it; moving focus away clears it. The keys stop at the ends.

Chart keys
KeyAction
Left arrow Right arrowPrevious / next point along x
Home EndFirst / last point along x
Up arrow Down arrowNext point up / down
EscapeDismiss the cursor

Screen readers

  • The chart's label names it, says how many points it has and lists these keys.
  • Each key press is announced in a polite live region. Moving the pointer is silent.
  • The table button under the chart shows its data as a table. Screen readers can reach the table even while it's hidden; clicking a row moves the cursor there and announces it.
  • With legend, each entry is a toggle button. The last one showing can't be hidden, but its button stays focusable.

On a touch screen, a tap pins the cursor and a tap outside the chart dismisses it.

See every scatter chart example