Centrim Design System — a token-first, accessible React component 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.
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.
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
| Month | Total volume |
|---|---|
| Jan | 420 |
| Feb | 465 |
| Mar | 512 |
| Apr | 498 |
| May | 574 |
| Jun | 640 |
| Jul | 688 |
| Aug | 712 |
| Sep | 690 |
| Oct | 756 |
| Nov | 812 |
| Dec | 864 |
ChartDataTable
The accessible alternative on its own. Rendered inside every chart’s “View data as table” panel by default.
| Region | This period | Previous period |
|---|---|---|
| North | 320 | 265 |
| South | 244 | 288 |
| East | 412 | 356 |
| West | 186 | 204 |
| Central | 298 | 250 |
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.
Last 12 months, sample data
Line chart of total volume rising steadily from 420 in January to 864 in December.
View data as table
| Month | Total volume |
|---|---|
| Jan | 420 |
| Feb | 465 |
| Mar | 512 |
| Apr | 498 |
| May | 574 |
| Jun | 640 |
| Jul | 688 |
| Aug | 712 |
| Sep | 690 |
| Oct | 756 |
| Nov | 812 |
| Dec | 864 |
Multiple series
Three series with distinct colours and dash patterns.
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
| Month | Category A | Category B | Category C |
|---|---|---|---|
| Jan | 420 | 260 | 180 |
| Feb | 465 | 288 | 194 |
| Mar | 512 | 305 | 176 |
| Apr | 498 | 340 | 210 |
| May | 574 | 366 | 232 |
| Jun | 640 | 392 | 205 |
| Jul | 688 | 430 | 248 |
| Aug | 712 | 452 | 262 |
| Sep | 690 | 468 | 240 |
| Oct | 756 | 502 | 276 |
| Nov | 812 | 528 | 288 |
| Dec | 864 | 560 | 302 |
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
Last 12 months, sample data
Area chart of cumulative volume increasing through the year from 420 to 864.
View data as table
| Month | Total volume |
|---|---|
| Jan | 420 |
| Feb | 465 |
| Mar | 512 |
| Apr | 498 |
| May | 574 |
| Jun | 640 |
| Jul | 688 |
| Aug | 712 |
| Sep | 690 |
| Oct | 756 |
| Nov | 812 |
| Dec | 864 |
Stacked
Data requirement: series that sum to a meaningful total.
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
| Month | Category A | Category B | Category C |
|---|---|---|---|
| Jan | 420 | 260 | 180 |
| Feb | 465 | 288 | 194 |
| Mar | 512 | 305 | 176 |
| Apr | 498 | 340 | 210 |
| May | 574 | 366 | 232 |
| Jun | 640 | 392 | 205 |
| Jul | 688 | 430 | 248 |
| Aug | 712 | 452 | 262 |
| Sep | 690 | 468 | 240 |
| Oct | 756 | 502 | 276 |
| Nov | 812 | 528 | 288 |
| Dec | 864 | 560 | 302 |
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
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
| Region | This period | Previous period |
|---|---|---|
| North | 320 | 265 |
| South | 244 | 288 |
| East | 412 | 356 |
| West | 186 | 204 |
| Central | 298 | 250 |
Stacked
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
| Region | This period | Previous period |
|---|---|---|
| North | 320 | 265 |
| South | 244 | 288 |
| East | 412 | 356 |
| West | 186 | 204 |
| Central | 298 | 250 |
Horizontal
Use when category labels are long or there are many categories.
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
| Region | This period |
|---|---|
| North | 320 |
| South | 244 |
| East | 412 |
| West | 186 |
| Central | 298 |
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
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
| Channel | Share |
|---|---|
| Direct | 38% |
| Referral | 24% |
| Search | 19% |
| Social | 12% |
| Other | 7% |
Empty
Share of total, sample data
No data to display
There are no values in the selected range yet.
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.
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
| Effort | Group A outcome |
|---|---|
| 8 | 22 |
| 14 | 31 |
| 21 | 38 |
| 27 | 52 |
| 33 | 58 |
| 41 | 71 |
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.
Without a value
Line only, for dense table cells where the value sits in an adjacent column.
With Metric
The existing Metric component stays the source of truth for KPI readouts; the sparkline only adds trend context.
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.