Charts
Scatter Chart
A relationship between two numeric measures, with every point independently named and positioned.
When to use it: Use it to reveal correlation, clusters, and outliers across two measurements.
Example
import {
ChartDescription,
ChartTable,
ChartTitle,
ChartTooltip,
ScatterChart,
ScatterChartPlot,
ScatterChartPoint,
} from "@comp0/react";
const initiatives = [
{ label: "Search", x: 3, y: 8 },
{ label: "Checkout", x: 7, y: 9 },
{ label: "Navigation", x: 4, y: 6 },
{ label: "Reporting", x: 8, y: 4 },
{ label: "Onboarding", x: 5, y: 7 },
] as const;
const formatScore = (value: number | Date) => `${value}/10`;
export function Example() {
return (
<ScatterChart
values={initiatives}
xLabel="Effort"
yLabel="Impact"
formatX={formatScore}
formatY={formatScore}
className="w-full max-w-2xl rounded-lg has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-4 has-[:focus-visible]:outline-teal-600 dark:has-[:focus-visible]:outline-teal-400"
>
<ChartTitle className="text-base font-semibold text-zinc-950 dark:text-zinc-50">
Initiative impact and effort
</ChartTitle>
<ScatterChartPlot
aria-label="Scatter chart comparing initiative impact and effort"
xMin={0}
xMax={10}
yMin={0}
yMax={10}
className="mx-auto mt-5 aspect-square w-full max-w-md overflow-visible"
>
{(point) => (
<ScatterChartPoint key={point.index} point={point} className="group outline-none">
<circle cx={point.x} cy={point.y} r="5" className="fill-transparent" />
<circle
cx={point.x}
cy={point.y}
r="2.4"
className="fill-teal-600 stroke-transparent group-data-active:stroke-zinc-950 dark:fill-teal-400 dark:group-data-active:stroke-white"
strokeWidth="2"
vectorEffect="non-scaling-stroke"
/>
</ScatterChartPoint>
)}
</ScatterChartPlot>
<ChartDescription className="mt-5 text-sm text-zinc-600 dark:text-zinc-400">
Search offers high impact for low effort; reporting is the least favorable tradeoff.
</ChartDescription>
<ChartTable className="mt-4 w-full border-collapse text-left text-sm [&_td]:border-t [&_td]:border-zinc-200 [&_td]:py-2 [&_th]:border-zinc-200 [&_th]:py-2 dark:[&_td]:border-zinc-800 dark:[&_th]:border-zinc-800 [&_caption]:sr-only" />
<ChartTooltip className="pointer-events-none z-50 rounded-md bg-zinc-950 px-2 py-1 text-sm text-white shadow-lg dark:bg-zinc-50 dark:text-zinc-950" />
</ScatterChart>
);
}Anatomy
Dashed frames are invisible state providers; shaded shapes own real DOM. Numbered pins match the list below.
A wireframe sketch of the assembled component. Each numbered marker matches a part in the list that follows.
ScatterChart
Native figure sharing labelled coordinates, axis labels, and formatting with every part. Owns a DOM element.
ChartTitle
Native figcaption that visibly names the figure. Owns a DOM element.
ScatterChartPlot / ScatterChartPoint
Independently positioned SVG points against two visible numeric axes. Owns a DOM element.
ChartDescription
Visible prose summarizing the important relationship. Owns a DOM element.
ChartTable
Native table that renders a complete default from the chart data: a caption from the ChartTitle text or axis labels, column headers, and one row per value. Pass children for a custom body. Owns a DOM element.
ChartTooltipOptional
Optional floating point label shown on hover or focus. Owns a DOM element.
Step by step
- 1
Add the main part
Start ScatterChart with labelled x and y coordinates and visible axis labels.
- 2
Add the supporting parts
Render ScatterChartPoint inside ScatterChartPlot so arrow keys move spatially between points.
- 3
Make the behavior clear
Add ChartDescription, a native ChartTable, and an optional ChartTooltip for exact values.
Exampletsx <ScatterChart values={initiatives} xLabel="Effort" yLabel="Impact"> <ChartTitle>Initiative impact and effort</ChartTitle> <ScatterChartPlot aria-label="Scatter chart comparing impact and effort"> {(point) => ( <ScatterChartPoint point={point}> <circle cx={point.x} cy={point.y} /> </ScatterChartPoint> )} </ScatterChartPlot> <ChartTable /> <ChartTooltip /> </ScatterChart>;
Keyboard
- ⇥
- Enters the plot at its current point and leaves with one more Tab.
- ←→↑↓
- Moves to the nearest point in the requested visual direction.
- Home
- Moves to the first point.
- End
- Moves to the last point.
- Esc
- Dismisses an open ChartTooltip.
Forms and accessibility
Charts are descriptive content and do not create form values.
Accessibility checklist
- Keep both numeric axis labels visible and use the same formatters in the table.
- Wrap every custom mark in ScatterChartPoint so it receives a formatted accessible name.
- Arrow keys move to the nearest point in the requested visual direction.
- Do not use point color as the only series or category distinction.
- Use ChartTooltip as an enhancement while retaining the native exact-value table.
API reference
import { ChartDescription, ChartTable, ChartTitle, ChartTooltip, ScatterChart, ScatterChartPlot, ScatterChartPoint } from "@comp0/react";ScatterChart
DOM elementNative figure sharing labelled coordinates, axis labels, and formatting with every part.
| Prop | Type | Description |
|---|---|---|
values | readonly ScatterChartValue[] | Finite x and y coordinates with non-empty point labels. |
xLabel | string | Visible headings for both numeric axes. |
yLabel | string | Visible headings for both numeric axes. |
formatX | (value) => string | Formatters shared by ticks, point names, and table cells. |
formatY | (value) => string | Formatters shared by ticks, point names, and table cells. |
ChartTitle
DOM elementNative figcaption that visibly names the figure.
ScatterChartPlot / ScatterChartPoint
DOM elementIndependently positioned SVG points against two visible numeric axes.
| Prop | Type | Description |
|---|---|---|
aria-label | string | Concise text alternative naming the relationship. |
xMin | number | Optional finite bounds that contain every point. |
xMax | number | Optional finite bounds that contain every point. |
yMin | number | Optional finite bounds that contain every point. |
yMax | number | Optional finite bounds that contain every point. |
xTickCount | number | Visible tick counts; each defaults to five. |
yTickCount | number | Visible tick counts; each defaults to five. |
children | (point: ScatterChartPointState) => ReactNode | Custom point renderer receiving its value and SVG position. |
point | ScatterChartPointState | Point state passed from the plot to ScatterChartPoint. |
ChartDescription
DOM elementVisible prose summarizing the important relationship.
ChartTable
DOM elementNative table that renders a complete default from the chart data: a caption from the ChartTitle text or axis labels, column headers, and one row per value. Pass children for a custom body.
ChartTooltip
OptionalDOM elementOptional floating point label shown on hover or focus.
| Prop | Type | Description |
|---|---|---|
placement | PopoverPlacement | Side of the active mark; defaults to "top". |
offset | number | Distance from the active mark; defaults to eight pixels. |
children | ReactNode | (details: ChartValueDetails) => ReactNode | Custom content receiving the active point's formatted details. |
Style hooks
Attributes that appear while a state is true. Target them with Tailwind data variants such as data-open:bg-zinc-100, or with any CSS selector.
ScatterChartPlot / ScatterChartPoint
| Style hook | Meaning |
|---|---|
[data-active] | The point currently reached by pointer or keyboard. |
ChartTooltip
| Style hook | Meaning |
|---|---|
[data-open] | A value tooltip is visible. |