Design System

Data visualization

One charting foundation for the whole system. Charts read only from the semantic chart tokens, work in light and dark themes, respect reduced motion, and always ship an accessible table alternative — never information that exists only as a picture.

Chart foundations

Every chart renders inside ChartContainer: a solid surface with a title, description, legend slot, a fluid plot area, the four lifecycle states and a collapsible data table. Series colours come from the chart-series-1…6 ramp, chrome from chart-grid, chart-axis and chart-label.

import { ChartContainer, ChartLegend, ChartTooltipContent, ChartDataTable } from "@/design-system";

When to use

  • Any chart in the system — it supplies the frame, states and accessible alternative.
  • Wrapping a bespoke Recharts composition so it inherits the system chrome.

When not to use

  • A single value with no trend — use Metric or Stat.
  • Precise value lookup or comparison of many rows — use Table or DataTable.

Chart palette

Six categorical series tokens, ordered for separation in hue and lightness so they survive greyscale and colour-vision deficiency. Chart chrome and positive/negative/neutral tokens sit alongside them.

chart-series-1
chart-series-2
chart-series-3
chart-series-4
chart-series-5
chart-series-6
chart-positive
chart-negative
chart-neutral

ChartLegend

Line indicators carry the dash pattern that identifies each series; swatches are used for filled marks. Pass onToggle to make entries filter series.

  • Category A
  • Category B
  • Category C
  • This period
  • Previous period

ChartTooltipContent

The chart tooltip presentation, shown here statically. It uses the shared soft-glass overlay surface so contextual chart tooltips match every other floating panel, while the chart body stays solid.

Sep

  • Category A690
  • Category B468
  • Category C240

Chart states

Loading, empty and error states reuse the existing Skeleton, EmptyState and ErrorState components — charts never define their own.

Volume over time

Last 12 months, sample data

Line chart of total volume across twelve months, rising from 420 in January to 864 in December.

View data as table
MonthTotal volume
Jan420
Feb465
Mar512
Apr498
May574
Jun640
Jul688
Aug712
Sep690
Oct756
Nov812
Dec864

ChartDataTable

The accessible alternative on its own. Rendered inside every chart’s “View data as table” panel by default.

Sample values by region for the current and previous period.
RegionThis periodPrevious period
North320265
South244288
East412356
West186204
Central298250

Accessibility

  • The plot area is role="img" described by a required written summary, so screen readers get the shape of the data.
  • Every chart offers a “View data as table” panel built on the real Table component.
  • Series are distinguished by dash pattern or point shape as well as colour; legends and tooltips always name the series.
  • Entrance animations are dropped under prefers-reduced-motion; tooltips and focus behaviour are preserved.

LineChart

Trends over a continuous scale, usually time. Grid lines are horizontal only and dash patterns separate multi-series lines.

import { LineChart } from "@/design-system";

When to use

  • Change over continuous time, at even intervals.
  • Comparing the shape of two to four trends.

When not to use

  • Comparing discrete, unordered categories — use BarChart.
  • Part-to-whole relationships — use DonutChart.
  • More than about five series, which becomes unreadable at any size.

Single series

Data requirement: one numeric value per ordered category.

Total volume

Last 12 months, sample data

Line chart of total volume rising steadily from 420 in January to 864 in December.

View data as table
MonthTotal volume
Jan420
Feb465
Mar512
Apr498
May574
Jun640
Jul688
Aug712
Sep690
Oct756
Nov812
Dec864

Multiple series

Three series with distinct colours and dash patterns.

Volume by category

Last 12 months, sample data

  • Category A
  • Category B
  • Category C

Line chart comparing three categories over twelve months. Category A grows fastest, Category B grows steadily, Category C stays broadly flat.

View data as table
MonthCategory ACategory BCategory C
Jan420260180
Feb465288194
Mar512305176
Apr498340210
May574366232
Jun640392205
Jul688430248
Aug712452262
Sep690468240
Oct756502276
Nov812528288
Dec864560302

Accessibility

  • Each series has its own dash pattern, so lines stay distinguishable without colour.
  • Data points expose values through the tooltip and the table panel.

AreaChart

A line with a restrained gradient fill, for when the volume beneath the line matters. Stack the series to show composition of a total.

import { AreaChart } from "@/design-system";

When to use

  • Cumulative or total volume over time.
  • Composition of a total over time, stacked.

When not to use

  • Comparing several unstacked series, where fills overlap and obscure each other.
  • Purely decorative emphasis on a single trend — a line is clearer.

Single series

Cumulative volume

Last 12 months, sample data

Area chart of cumulative volume increasing through the year from 420 to 864.

View data as table
MonthTotal volume
Jan420
Feb465
Mar512
Apr498
May574
Jun640
Jul688
Aug712
Sep690
Oct756
Nov812
Dec864

Stacked

Data requirement: series that sum to a meaningful total.

Composition of total volume

Last 12 months, sample data

  • Category A
  • Category B
  • Category C

Stacked area chart showing three categories summing to total volume, which grows from about 860 in January to about 1,726 in December.

View data as table
MonthCategory ACategory BCategory C
Jan420260180
Feb465288194
Mar512305176
Apr498340210
May574366232
Jun640392205
Jul688430248
Aug712452262
Sep690468240
Oct756502276
Nov812528288
Dec864560302

Accessibility

  • Fills stay under 30% opacity so the stroke and the grid remain visible in both themes.
  • Values are available in the tooltip and the table panel.

BarChart

Comparison of discrete categories, vertical or horizontal, grouped or stacked. Corner rounding is deliberately minimal so bar lengths stay easy to compare.

import { BarChart } from "@/design-system";

When to use

  • Comparing values across discrete, unordered categories.
  • Long category names — switch to horizontal orientation.
  • Composition of a total per category — stack the series.

When not to use

  • Continuous time series with many points — use LineChart.
  • Truncating the value axis, which exaggerates differences.

Grouped

Volume by region

Current versus previous period, sample data

  • This period
  • Previous period

Grouped bar chart comparing five regions across two periods. East is highest at 412 this period; South and West fell versus the previous period.

View data as table
RegionThis periodPrevious period
North320265
South244288
East412356
West186204
Central298250

Stacked

Combined volume by region

Two periods stacked, sample data

  • This period
  • Previous period

Stacked bar chart of combined volume per region, ranging from 390 in West to 768 in East.

View data as table
RegionThis periodPrevious period
North320265
South244288
East412356
West186204
Central298250

Horizontal

Use when category labels are long or there are many categories.

Volume by region

Current period, sample data

Horizontal bar chart of current-period volume by region, from 186 in West to 412 in East.

View data as table
RegionThis period
North320
South244
East412
West186
Central298

Accessibility

  • Grouped bars pair colour with a named legend entry and the table panel.
  • The value axis always starts at zero so lengths are comparable.

DonutChart

Simple part-to-whole relationships with a limited number of categories, with an optional total in the centre. Preferred over a pie chart.

import { DonutChart } from "@/design-system";

When to use

  • Two to six categories that sum to a meaningful whole.
  • Showing a headline total alongside its breakdown.

When not to use

  • More than about six slices — use a horizontal bar chart.
  • Comparing precise values, or values that do not sum to a whole.
  • Change over time.

With centre value

Volume by channel

Share of total, sample data

  • Direct · 38%
  • Referral · 24%
  • Search · 19%
  • Social · 12%
  • Other · 7%

Donut chart of channel share: Direct 38 percent, Referral 24 percent, Search 19 percent, Social 12 percent, Other 7 percent.

View data as table
ChannelShare
Direct38%
Referral24%
Search19%
Social12%
Other7%

Empty

Volume by channel

Share of total, sample data

No data to display

There are no values in the selected range yet.

Accessibility

  • Each legend entry names the category and prints its value, so slices are never identified by colour alone.
  • Slices are separated by a surface-coloured stroke, which keeps boundaries visible in both themes.

ScatterChart

The relationship between two numerical variables. Each series gets a distinct point shape as well as a colour.

import { ScatterChart } from "@/design-system";

When to use

  • Looking for correlation, clustering or outliers across two measures.
  • Comparing the distribution of two or three groups.

When not to use

  • Categorical comparison — use BarChart.
  • A single ordered series over time — use LineChart.

Two series

Data requirement: an x and a y numeric value per point.

Outcome against effort

Sample data, two groups

  • Group A
  • Group B

Scatter chart of outcome against effort for two groups. Group A rises more steeply than Group B, suggesting a stronger relationship.

View data as table
Group A values. Group B follows the same shape at lower outcomes.
EffortGroup A outcome
822
1431
2138
2752
3358
4171

Accessibility

  • Point shape distinguishes series independently of colour.
  • Both axes are labelled with the measure they represent.

Sparkline

Compact trend context with no axes or grid. Pairs with the existing Metric component rather than duplicating it.

import { Sparkline } from "@/design-system";

When to use

  • Trend context beside a KPI, in a table cell or in a list row.
  • Showing direction of travel where exact values are not the point.

When not to use

  • Reading precise values — use a full chart with axes.
  • As the only indicator of direction: always pair it with a value or delta.

Tones

Positive, negative and neutral, each with an arrow and delta so direction never depends on colour.

33+18.2%
Trend rising over the last nine periods, ending at 33.
22−12.4%
Trend falling over the last nine periods, ending at 22.
250.0%
Trend broadly flat over the last nine periods, holding around 25.

Without a value

Line only, for dense table cells where the value sits in an adjacent column.

Trend rising over the last nine periods.

With Metric

The existing Metric component stays the source of truth for KPI readouts; the sparkline only adds trend context.

Total volume864Increase: +6.4%Versus previous month
Total volume trending up over nine months.
Open items22Decrease: −12.4%Versus previous month
Open items trending down over nine months, which is an improvement.
Average duration25mNo change: 0.0%Versus previous month
Average duration flat over nine months.

Accessibility

  • Tone is always accompanied by a direction arrow and a delta label, never colour alone.
  • A written summary is required and read by screen readers in place of the line.

Choosing a chart

Pick the chart that matches the question being asked, not the one that looks most appealing. A table is often the right answer.

Guidance only — no component.

Guidance

LineChart
Trends over continuous time.
AreaChart
Volume or cumulative totals over time; stacked for composition.
BarChart
Comparing discrete categories; stacked for composition of a total.
DonutChart
Simple part-to-whole with a limited number of categories.
ScatterChart
Relationships between two numerical variables.
Sparkline
Compact trend context beside a value.
Table / DataTable
Precise values, many rows, or when the reader needs to scan and compare exactly.

Responsive behaviour

  • Plot areas are fluid; below the minimum plot width the chart scrolls horizontally rather than crushing labels.
  • Legends wrap onto multiple lines and, on narrow viewports, sit above the plot so they never steal plot width.
  • Long category names should use the horizontal bar orientation instead of rotated ticks.
  • The table alternative is collapsed by default, so it costs nothing on small screens but is always one tap away.

Light and dark

Every chart colour resolves from a CSS custom property, so switching theme with the toggle in the header re-renders series, grid, axis, label and tooltip colours with no chart-level code. The dark ramp is designed rather than inverted: hues lift in lightness and drop in saturation so they read against the dark surface without glowing.