AreaChart
A line with the area under it washed in its colour: one quantity over time, or a whole split into the parts it is made of.
CI minutes by repository
Per month, stacked ยท sample data
- cennge-ui
- playwright-msi-scraper
- payload-cache
- ai-niche-scout
- spredo-crypto-screener
Arrow keys move between the values.
| Month | cennge-ui | playwright-msi-scraper | payload-cache | ai-niche-scout | spredo-crypto-screener |
|---|---|---|---|---|---|
| Mar | 820 | 310 | 240 | 180 | 150 |
| Apr | 910 | 290 | 260 | 210 | 140 |
| May | 1,040 | 340 | 230 | 260 | 170 |
| Jun | 1,180 | 360 | 280 | 240 | 190 |
| Jul | 1,320 | 330 | 310 | 220 | 160 |
| Aug | 1,250 | 380 | 290 | 270 | 180 |
| Sep | 1,460 | 410 | 330 | 300 | 200 |
| Oct | 1,610 | 390 | 350 | 280 | 210 |
Requests to payload-cache
Every two hours, yesterday ยท sample data
Arrow keys move between the values.
| From | Requests |
|---|---|
| 00:00 | 2,180 |
| 02:00 | 1,650 |
| 04:00 | 1,920 |
| 06:00 | 5,100 |
| 08:00 | 8,600 |
| 10:00 | 9,300 |
| 12:00 | 9,050 |
| 14:00 | 9,350 |
| 16:00 | 8,200 |
| 18:00 | 6,100 |
| 20:00 | 4,400 |
| 22:00 | 3,100 |
- Stacked, each part fills down to the one below it, so the top line is the total.
- Hover a month or tap it: the readout gives each part's own value, not the running sum.
- Tab to a chart: the arrows move the crosshair, Home and End jump to the ends, Escape hides it.
Field marks
- Import
import { AreaChart } from "cennge-ui/charts";- Size
- 5.6 KB gzip for this import, its dependencies aside
- Pattern
- A
<figure>with a focusable group over the plot and a hidden<table>of the data - Keyboard
- Arrow left Arrow right Arrow up Arrow down HomeEndEsc
- SSR
- Rendered on the server in the Next.js check; hydrates without a warning
- Fill
- The series' colour, fading towards the floor; the axis takes in zero unless you set
domain - Tests
- Unit tests; end-to-end in Chromium, Firefox and WebKit, on desktop and phone viewports: line-chart
- Source
- src/charts
When to use#
Use an AreaChart for one quantity over time when its size matters as much as its shape: the wash down to zero gives the line weight. With isStacked it shows how a whole is split, and how that split changes: minutes per repository adding up to the month's total.
Similar#
- LineChart is the same chart without the wash. Its axis can fit the data with
startAtZero={false}; an AreaChart has no such prop, and its axis takes in zero unless you set adomain. - BarChart stacks parts too, one bar per category, when the categories are not a sequence in time.
- DonutChart splits a single whole, at one moment, into parts.
One quantity#
A single series has no legend; the aria-label names the chart and its table. The numbers on this page are sample data.
Arrow keys move between the values.
| Month | Minutes |
|---|---|
| Mar | 1,700 |
| Apr | 1,810 |
| May | 2,040 |
| Jun | 2,250 |
| Jul | 2,340 |
| Aug | 2,370 |
| Sep | 2,700 |
| Oct | 2,840 |
import { AreaChart } from "cennge-ui/charts";
const months = [
{ month: "Mar", minutes: 1700 },
{ month: "Apr", minutes: 1810 },
{ month: "May", minutes: 2040 },
{ month: "Jun", minutes: 2250 },
{ month: "Jul", minutes: 2340 },
{ month: "Aug", minutes: 2370 },
{ month: "Sep", minutes: 2700 },
{ month: "Oct", minutes: 2840 },
];
export default function CiMinutes() {
return (
<AreaChart
aria-label="CI minutes per month"
data={months}
index="month"
indexLabel="Month"
series={[{ key: "minutes", label: "Minutes" }]}
className="h-56"
/>
);
}Stacked, in colours that mean something#
isStacked piles the series up in the order of series, the first at the bottom. Each part fills down to the one below it, so the top edge is the total, and the readout and the table give each part's own value. In a stack a missing value counts as zero, so the parts above it still rest on something.
Without a color a series takes the palette slot of its place, as on the plate. Here theme colours carry a meaning instead: passed in success, flaky in warning, failed in danger.
- Passed
- Flaky
- Failed
Arrow keys move between the values.
| Week of | Passed | Flaky | Failed |
|---|---|---|---|
| Aug 17 | 52 | 4 | 6 |
| Aug 24 | 58 | 3 | 4 |
| Aug 31 | 55 | 6 | 7 |
| Sep 7 | 61 | 5 | 3 |
| Sep 14 | 64 | 2 | 5 |
| Sep 21 | 59 | 4 | 8 |
| Sep 28 | 68 | 3 | 4 |
| Oct 5 | 71 | 5 | 3 |
| Oct 12 | 66 | 2 | 6 |
| Oct 19 | 74 | 3 | 2 |
| Oct 26 | 79 | 1 | 3 |
| Nov 2 | 83 | 2 | 2 |
import { AreaChart } from "cennge-ui/charts";
const weeks = [
{ week: "Aug 17", passed: 52, flaky: 4, failed: 6 },
{ week: "Aug 24", passed: 58, flaky: 3, failed: 4 },
{ week: "Aug 31", passed: 55, flaky: 6, failed: 7 },
{ week: "Sep 7", passed: 61, flaky: 5, failed: 3 },
{ week: "Sep 14", passed: 64, flaky: 2, failed: 5 },
{ week: "Sep 21", passed: 59, flaky: 4, failed: 8 },
{ week: "Sep 28", passed: 68, flaky: 3, failed: 4 },
{ week: "Oct 5", passed: 71, flaky: 5, failed: 3 },
{ week: "Oct 12", passed: 66, flaky: 2, failed: 6 },
{ week: "Oct 19", passed: 74, flaky: 3, failed: 2 },
{ week: "Oct 26", passed: 79, flaky: 1, failed: 3 },
{ week: "Nov 2", passed: 83, flaky: 2, failed: 2 },
];
export default function CiRunsByResult() {
return (
<AreaChart
aria-label="CI runs per week by result"
data={weeks}
index="week"
indexLabel="Week of"
isStacked
series={[
{ key: "passed", label: "Passed", color: "success" },
{ key: "flaky", label: "Flaky", color: "warning" },
{ key: "failed", label: "Failed", color: "danger" },
]}
className="h-64"
/>
);
}A smooth curve#
curve="smooth" bends through the points without overshooting them, so the wash never dips below zero where spredo-crypto-screener stops scanning, and stays flat between two equal values.
Arrow keys move between the values.
| Hour | Scans |
|---|---|
| 06:00 | 0 |
| 08:00 | 140 |
| 10:00 | 410 |
| 12:00 | 410 |
| 14:00 | 380 |
| 16:00 | 520 |
| 18:00 | 260 |
| 20:00 | 0 |
| 22:00 | 0 |
import { AreaChart } from "cennge-ui/charts";
const hours = [
{ hour: "06:00", scans: 0 },
{ hour: "08:00", scans: 140 },
{ hour: "10:00", scans: 410 },
{ hour: "12:00", scans: 410 },
{ hour: "14:00", scans: 380 },
{ hour: "16:00", scans: 520 },
{ hour: "18:00", scans: 260 },
{ hour: "20:00", scans: 0 },
{ hour: "22:00", scans: 0 },
];
export default function ScreenerScans() {
return (
<AreaChart
aria-label="Screener scans every two hours"
data={hours}
index="hour"
indexLabel="Hour"
curve="smooth"
series={[{ key: "scans", label: "Scans" }]}
className="h-56"
/>
);
}Without axes, in a card#
showXAxis, showYAxis and showGrid turn off for a chart that sits under a number. The readout, the keyboard and the table stay: hover the chart to read the requests in each two hours. On a Card the dots of the readout sit in a ring of its surface by themselves.
Requests to payload-cache yesterday
68,950
Arrow keys move between the values.
| From | Requests |
|---|---|
| 00:00 | 2,180 |
| 02:00 | 1,650 |
| 04:00 | 1,920 |
| 06:00 | 5,100 |
| 08:00 | 8,600 |
| 10:00 | 9,300 |
| 12:00 | 9,050 |
| 14:00 | 9,350 |
| 16:00 | 8,200 |
| 18:00 | 6,100 |
| 20:00 | 4,400 |
| 22:00 | 3,100 |
import { Card, CardBody } from "cennge-ui";
import { AreaChart } from "cennge-ui/charts";
// Requests in each two hours, from midnight.
const perTwoHours = [
2180, 1650, 1920, 5100, 8600, 9300,
9050, 9350, 8200, 6100, 4400, 3100,
];
const requests = perTwoHours.map((count, i) => ({ hour: `${String(i * 2).padStart(2, "0")}:00`, count }));
const total = perTwoHours.reduce((sum, count) => sum + count, 0);
export default function RequestsCard() {
return (
<Card variant="flat" className="w-full max-w-sm">
<CardBody>
<p className="text-sm text-muted">Requests to payload-cache yesterday</p>
<p className="text-2xl font-semibold tabular-nums">{total.toLocaleString("en-US")}</p>
<AreaChart
aria-label="Requests to payload-cache every two hours"
data={requests}
index="hour"
indexLabel="From"
curve="smooth"
series={[{ key: "count", label: "Requests" }]}
showXAxis={false}
showYAxis={false}
showGrid={false}
className="mt-2 h-24"
/>
</CardBody>
</Card>
);
}Accessibility#
- The figure and its plot are named by
aria-label; the plot is a focusable group described bylabels.keyboardHint. - Focus from the keyboard puts the crosshair on the first point, and a polite live region reads each place as the arrows move it, as "Apr: cennge-ui 910, playwright-msi-scraper 290, payload-cache 260, ai-niche-scout 210, spredo-crypto-screener 140".
- Screen readers get every value in a table captioned with the chart's name. In a stack the table holds each part, never the sums.
- The legend names each series beside a sample of its colour, so colour never says which is which alone.
Server rendering#
The legend, the axes, the grid and the data table come from the server. The areas are drawn once the browser has measured the plot and draw in from the left; with reduced motion they appear at once.
Props#
Each entry of series is { key, label, color }: the field that holds its values, its name (the key without one), and a palette slot from chart-1 to chart-8, a theme colour or any CSS colour.