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 className="sr-only">Initiative impact and effort values</caption>
<thead>
<tr>
<th scope="col">Initiative</th>
<th scope="col">Effort</th>
<th scope="col">Impact</th>
</tr>
</thead>
<tbody>
{initiatives.map((initiative) => (
<tr key={initiative.label}>
<th scope="row">{initiative.label}</th>
<td>{formatScore(initiative.x)}</td>
<td>{formatScore(initiative.y)}</td>
</tr>
))}
</tbody>
</ChartTable>
<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 composed from the same labelled coordinates. 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> <caption>Initiative values</caption> </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 composed from the same labelled coordinates.
ChartTooltip
OptionalDOM elementOptional floating point label shown on hover or focus.
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. |