ScatterChart
Two measures per point, drawn against each other, for reading whether they move together. A point that also carries a third number is drawn as a bubble, so a scatter plot and a bubble chart are one component.
import { ScatterChart } from 'neba';
<ScatterChart
label="Pages read against session length"
xAxis={{ label: 'Seconds on site' }}
yAxis={{ label: 'Pages' }}
series={[
{
name: 'Organic',
data: [
{ x: 22, y: 2 },
{ x: 41, y: 3 },
{ x: 55, y: 4 }
]
}
]}
/>;The data
The series shape is the one every chart shares (see LineChart for the full definition), with one requirement of its own: every point carries an x, and that x is a number or a Date. Both axes measure here, so a bare number as a datum has nothing to be placed against; a point given a string x is not on a number line and the chart draws its empty state rather than a row of marks at zero.
{ x: 22, y: 2 } // a dot
{ x: 22, y: 2, z: 180 } // a bubble
{ x: 22, y: null } // a gap: no mark, and an empty cell in the tablecategories supplies the x of a point that does not carry one, found by index.
z is optional and is read as an area. A point without it is drawn at pointRadius; a point with it is scaled under maxRadius by the square root of its share, so a value four times as large draws a bubble twice as wide.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| series * | NebaChartSeries[] | — | The series. Every point carries both an x and a y as numbers, and one that also carries a z is drawn as a bubble rather than a dot |
| categories | (number | Date)[] | — | The x of a point that does not carry its own, found by index. A string is not a place on a number line, so it is not accepted here |
| xAxis | NebaChartAxis | — | The x axis. Here it is a second value axis rather than a category axis, so it ticks at rounded numbers rather than at the data, and it casts a grid by default |
| yAxis | NebaChartAxis | — | The value axis |
| shape | 'auto' | 'varied' | 'circle' | 'square' | 'triangle' | 'diamond' | 'cross' | 'auto' | What each mark is drawn as. auto is a circle while colour alone can carry identity and a shape per series from the fourth on; varied always separates them, which is what a chart that will be printed needs |
| pointRadius | number | size | The radius of a mark with no z, in pixels |
| maxRadius | number | plot 짧은 변의 1/12 / a twelfth of the plot | The radius of the largest bubble, in pixels. Everything else is scaled under it by area, not by radius: encode z as a radius and a value twice as large draws a mark four times the size |
| variantshared | 'solid' | 'outline' | 'text' | 'text' | Weight of the surface. A chart is a drawing rather than a sheet, so this defaults to text and a chart inside a Card draws no second edge. Use outline for one that stands alone |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Axis type, line weight, marker size, and the height, when none is given |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | The sheet's colour family. A series' colour does not come from here: the palette or series.color decides that |
| densityshared | 'default' | 'compact' | 'default' | Padding only: never the height, never the type scale |
| elevationshared | 0 | 1 | 2 | 3 | 0 | Drop shadow depth. 0 means no shadow at all |
| height | number | string | size | How tall the drawing is. The axis labels are drawn inside it, so a card sized to the chart is a card the chart fits in |
| label | string | locale's word | The chart's accessible name. Read out in place of the drawing, and the caption of the hidden data table under it. Without it the locale's generic word stands in, but what the chart is *of* can only be said here |
| format | Intl.NumberFormatOptions | — | How numbers are written everywhere they appear: the axis, the tooltip, the value labels, the table. Without it, past ten thousand they are compacted (12.4K) |
| locale | string | — | The language of the chart's own words and dates |
| legend | boolean | NebaChartLegend | series ≥ 2 | Shown automatically from two series up and left off below that: a legend with one swatch restates the title |
| tooltip | boolean | NebaChartTooltip | true | What the pointer uncovers. It never carries a value that is not also in the hidden table |
| empty | ReactNode | — | What to draw when there is nothing to draw |
Every native <div> attribute passes through, along with every Box prop. xAxis, yAxis, legend and tooltip take the same shapes they take on LineChart. See prop conventions for the shared axes.
Examples
Bubbles
A z on a point turns it into a bubble. maxRadius is the radius of the largest one and everything else is scaled under it; leave it out and it is a twelfth of the plot's short side.
The scale is taken across every series and does not move when the legend is filtered, so two bubbles the same size mean the same number wherever they are.
shape
Scatter is the form where any two marks can end up side by side, so the palette has to separate on every pair rather than on the pairs that touch, and run that way it separates three series. Past three, shape="auto" gives each series a mark of its own, in the fixed order circle, square, triangle, diamond, cross. The legend shows the same shapes.
shape="varied" turns that on from the first series, which is what a chart that will be printed or read in greyscale wants. Naming one of the five shapes uses it for every mark; on four or more series that is opting out of the second channel, so do it only when each series carries a color of its own.
xAxis · yAxis
The x axis is a value axis here, not a category axis: it ticks at rounded numbers rather than at the data, and it casts a grid. Reading a mark's x off the picture is half of what the chart is for. Setting grid to false on xAxis turns it off.
Neither axis is forced through zero. What a position encodes is a place, so cropping a scale slides every mark by the same amount and the shape of the cloud survives; min and max pin an axis where a comparison needs a fixed frame.
format belongs to the value axis, so xAxis.tickFormat is how the x ticks are written.
tooltip
There are no shared categories to gather, so the tooltip is always about one mark: the pointer picks the nearest one within its own radius plus 24px, and there is no crosshair. The panel's heading is the mark's x and its row is the series and the y; a point's own label replaces the value, and tooltip.render replaces the panel.
<ScatterChart tooltip={{ render: ({ category, items }) => … }} … />Colour
A series takes its palette slot from its place in the series array, so filtering the legend never repaints the survivors. series.color overrides the slot with a colour family or any CSS colour, and a point's own color overrides it for that one mark.
Accessibility
- The data is also rendered as a visually hidden table, captioned with
label, with one row per point and its columns named from the axis labels. - The plot is focusable;
←/→walk the marks in the order the data was given,Home/Endjump to the ends,Escapeclears the tooltip. - Past three series, identity is carried by shape as well as by colour: which is what makes the chart readable under colour vision deficiency, in greyscale and in print.