Area Chart
A trend over time: a smooth line with a gradient fill, plus LineChart for comparing series without fills.
Preview
Revenue this year
$328,700
12 points, Jan to Dec. Revenue: low $18,400, high $38,700, last $38,700.
| Month | Revenue |
|---|---|
| Jan | $18,400 |
| Feb | $21,200 |
| Mar | $19,800 |
| Apr | $24,600 |
| May | $23,100 |
| Jun | $27,900 |
| Jul | $26,400 |
| Aug | $31,200 |
| Sep | $29,800 |
| Oct | $34,500 |
| Nov | $33,100 |
| Dec | $38,700 |
Installation
pnpm add @syntara/react @syntara/tokensimport { AreaChart, LineChart } from '@syntara/react';Usage
import { AreaChart } from '@syntara/react';
export function Revenue({ data }: { data: { month: string; revenue: number }[] }) {
return (
<AreaChart
aria-label="Revenue by month"
data={data}
x="month"
series={[{ key: 'revenue', label: 'Revenue' }]}
format={{ value: { style: 'currency', currency: 'USD', maximumFractionDigits: 0 } }}
/>
);
}Examples
Two series
Overlapping areas with a legend; the readout lists every series at that x.
- Income
- Spending
8 points, W1 to W8. Income: low $3,900, high $6,100, last $5,900. Spending: low $2,900, high $4,100, last $3,500.
| Week | Income | Spending |
|---|---|---|
| W1 | $4,200 | $3,100 |
| W2 | $3,900 | $3,600 |
| W3 | $5,100 | $2,900 |
| W4 | $4,700 | $3,800 |
| W5 | $5,600 | $3,300 |
| W6 | $5,200 | $4,100 |
| W7 | $6,100 | $3,700 |
| W8 | $5,900 | $3,500 |
Line chart
LineChart with three series and dots on each point.
- Web
- iOS
- Android
7 points, Mon to Sun. Web: low 980, high 1,520, last 980. iOS: low 860, high 1,310, last 1,310. Android: low 720, high 1,080, last 1,080.
| Day | Web | iOS | Android |
|---|---|---|---|
| Mon | 1,240 | 860 | 720 |
| Tue | 1,380 | 910 | 780 |
| Wed | 1,310 | 990 | 810 |
| Thu | 1,460 | 1,040 | 900 |
| Fri | 1,520 | 1,120 | 940 |
| Sat | 1,090 | 1,260 | 1,010 |
| Sun | 980 | 1,310 | 1,080 |
Minimal
Linear curve, no grid and no y axis, for a compact trend.
8 points, 09:00 to 16:00. Requests: low 320, high 690, last 690.
| Hour | Requests |
|---|---|
| 09:00 | 320 |
| 10:00 | 410 |
| 11:00 | 385 |
| 12:00 | 520 |
| 13:00 | 470 |
| 14:00 | 610 |
| 15:00 | 580 |
| 16:00 | 690 |
Table view
A switch that shows the chart's data table.
4 points, Q1 to Q4. Balance: low €12,400, high €19,300, last €19,300.
| Quarter | Balance |
|---|---|
| Q1 | €12,400 |
| Q2 | €15,800 |
| Q3 | €14,900 |
| Q4 | €19,300 |
Accessibility
| Keys | Action |
|---|---|
| Tab | Moves focus into the chart (one tab stop), to the last point you visited or the first. |
| ArrowRightorArrowLeft | Moves to the next / previous x position and shows its readout. Mirrored in right-to-left locales. |
| HomeorEnd | Jumps to the first / last x position. |
| Escape | Hides the readout without moving focus (WCAG 1.4.13). The next move shows it again. |
- The plot's hit layer is a React Aria ListBox (horizontal): one option per x position, named with its x and every series value, e.g. "Mar: Revenue $19,800, Costs $13,100". Focusing an option makes the screen reader read it, so no live region is needed.
- The ListBox is named by aria-label or aria-labelledby (one is required) and described by a one-sentence summary (generated from the data, or the summary prop).
- Every chart also renders its data as a <table> with a caption, visually hidden unless showTable. No value is only reachable by hovering.
- The SVG, axis labels and the floating readout are aria-hidden: they repeat what the options and the table say.
- Pointer, pen and touch snap to the nearest x position (full-height bands), so nobody has to land on a 2px line or a thin bar.
- Text never wears a series colour: labels, axes, legend and readout use text.default and text.subtle; the series colour is only on marks and swatches. Series colours come from --syntara-chart-1..4, solved per brand and scheme to reach 3:1 on surfaces and stay distinguishable under colour-vision deficiency.
- A 2px focus ring surrounds the plot and axes while an x position has keyboard focus.
- Motion (line draw-in, area fade-up, bar growth, readout glide) sits behind prefers-reduced-motion: no-preference.
- There are no disabled or invalid states: a chart is read-only.
Guidelines
Do
- Name the chart with aria-labelledby when a card title already says what it shows.
- Use AreaChart for one or two series over time; LineChart when comparing three or four.
- Format values with Intl options so the readout, table and names read naturally in every locale.
- Pass null for a missing value: the line breaks instead of dropping to zero.
Don’t
- Don't plot more than 4 series; fold the tail into "Other" or use small multiples.
- Don't put two y scales on one chart; index both series to a common base or use two charts.
- Don't colour text with series colours; the legend and readout already key each series.
- Don't rely on the chart alone for a number that matters; state it in a heading or stat.
API reference
AreaChart
aria-label | aria-labelledbyRequiredstringNames the chart. One is required; use aria-labelledby to point at a visible card title.
dataRequiredT[]One object per x position, in order.
xRequiredkeyof TKey of the x value in each row (a month name, a Date, a number).
seriesRequired{ key: string; label: string; color?: string }[]What to plot, in palette order (--syntara-chart-1..4). Up to 4; past that, fold the tail into "Other". color overrides the palette with a token.
format{ value?: Intl.NumberFormatOptions | fn; axis?: …; x?: Intl.DateTimeFormatOptions | fn }Formats values (readout, table, names), y ticks (default: value in compact notation) and x values (Dates default to "12 Mar").
heightnumberHeight of the plot plus the x axis, in px. The legend sits above it.
Default
240curvesmoothdefaultlinear
smooth is monotone cubic: it never overshoots the data, so a peak is never drawn higher than it was.
showGridbooleanHorizontal hairlines at each y tick.
Default
trueshowXAxisbooleanX labels, thinned to what fits (always the first and last).
Default
trueshowYAxisbooleanY tick labels in a gutter at the inline start.
Default
trueyDomain[number, number]Fixes the y range. Default: nice round numbers around the data, from 0 for areas; fitted for LineChart.
showLegendbooleanLegend above the plot.
Default
series.length > 1showTablebooleanShows the data table under the chart instead of keeping it visually hidden.
Default
falsexLabelstringHeader of the table's first column, e.g. "Month".
summarystringOne sentence describing the data for screen readers. Default: generated from the data.
glowbooleanA soft halo in the series colour under each line. Decorative.
Default
trueshowDotsbooleanRinged dots on every data point.
Default
false
LineChart
…AreaChartPropsSame props as AreaChart. No fill, and the y range is fitted to the data instead of starting at 0.
Tokens
The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.
- Colour8
text.defaulttext.subtleborder.defaultborder.subtlesurface.raisedfocus.ringtext.brandaccent.text- Type6
font.bodyfont.size.xsfont.size.smfont.tracking.xsfont.tracking.smfont.weight.semibold- Space and size4
space.1space.2space.3space.4- Shape3
radius.containerradius.fieldradius.pill- Depth4
hairlineshadow.overlayshadow.highlightshadow.raised- Motion7
motion.duration.fastmotion.duration.normalmotion.duration.slowmotion.easingmotion.easingOutmotion.springmotion.duration.spring- Other6
chart.1chart.2chart.3chart.4chart.gridchart.axis