Chart
Line, area, bar, pie, donut and sparkline charts, drawn from a plain HTML table, or from data in JavaScript, with no chart library. The table stays as the chart’s data view, so every number is readable without the chart.
- CSS
css/components/chart.css- JS
js/components/chart.js- Docs
docs/charts.md
01 — Chart types
Line chart
Change over time, for up to five series. Hover or tap a point for its card; focus the chart and use the arrow keys.
| Month | Classic | Electric | Cargo |
|---|---|---|---|
| Jan | 120,400 | 61,200 | 3,100 |
| Feb | 118,200 | 63,900 | 3,300 |
| Mar | 141,900 | 78,400 | 4,200 |
| Apr | 152,300 | 92,100 | 5,600 |
| May | 168,700 | 108,600 | 6,900 |
| Jun | 171,200 | 117,300 | 7,400 |
| Jul | 176,500 | 124,900 | 7,900 |
| Aug | 169,800 | 121,400 | 7,600 |
| Sep | 158,300 | 116,800 | 7,100 |
Stacked area chart
data-type="area" data-stacked: how parts add up to a total over time.
| Time | Members | Day passes | Single rides |
|---|---|---|---|
| 6 am | 420 | 40 | 60 |
| 8 am | 1,380 | 90 | 180 |
| 10 am | 610 | 210 | 160 |
| 12 pm | 720 | 380 | 240 |
| 2 pm | 680 | 420 | 230 |
| 4 pm | 1,120 | 360 | 260 |
| 6 pm | 1,450 | 290 | 310 |
| 8 pm | 640 | 210 | 220 |
| 10 pm | 260 | 80 | 120 |
Bar chart
Comparing amounts across categories.
| Year | Stations |
|---|---|
| 2019 | 6 |
| 2020 | 9 |
| 2021 | 4 |
| 2022 | 12 |
| 2023 | 15 |
| 2024 | 11 |
| 2025 | 18 |
| 2026 | 21 |
Grouped bar chart
Two or more series side by side in each category.
| Day | 2025 | 2026 |
|---|---|---|
| Mon | 7,820 | 8,640 |
| Tue | 8,110 | 8,920 |
| Wed | 8,240 | 9,180 |
| Thu | 8,050 | 8,970 |
| Fri | 7,630 | 8,410 |
| Sat | 6,120 | 7,050 |
| Sun | 5,480 | 6,230 |
Pie chart
Parts of one whole, up to six slices.
| Purpose | Share of trips |
|---|---|
| Commuting | 46% |
| Leisure | 24% |
| Errands | 18% |
| Exercise | 12% |
Donut chart
A pie with the total in the middle. Set the center with data-center-label.
| Bike type | Bikes |
|---|---|
| Classic | 620 |
| Electric | 340 |
| Cargo | 40 |
Sparkline
A tiny trend line for inside a stat. It takes its data from attributes and says the trend in its aria-label.
- Members
- 12.4K
02 — Options
Attributes
| Option | Values |
|---|---|
data-type | line, area, bar, pie, donut, sparkline |
data-stacked | Stack area or bar series. |
data-prefix, data-suffix | For axis ticks: $, %, h. |
data-height | Plot height in px (default 260). |
data-center-label, data-center-value | The donut’s center (default: Total and the sum). |
03 — From data
Live data
For apps that hold the numbers themselves, such as React state or a live feed. Jewel.chart.render() writes the table and draws the chart; update() redraws it in place. The table is still there behind “Show data”, so the keyboard and screen reader view keep working. Press the button to send new numbers.
const chart = Jewel.chart.render(document.querySelector('#pods'), {
type: 'donut',
title: 'Pods by state',
categoryLabel: 'State',
labels: ['Running', 'Pending', 'Failed'],
series: [{ name: 'Pods', values: [12, 5, 1] }],
});
// Later, when the data changes:
chart.update({ series: [{ values: [14, 3, 1] }] });
In React
Render once in an effect, call update() when the data changes, and let Jewel own the element’s contents.
function PodsChart({ counts }) {
const ref = useRef(null);
const chart = useRef(null);
useEffect(() => {
chart.current = Jewel.chart.render(ref.current, {
type: 'bar', categoryLabel: 'State',
labels: ['Running', 'Pending', 'Failed'],
series: [{ name: 'Pods', values: counts }],
});
}, []);
useEffect(() => { chart.current?.update({ series: [{ values: counts }] }); }, [counts]);
return <div ref={ref} />;
}
Data shape
| Field | What it is |
|---|---|
type | line, area, bar, pie, donut or sparkline. Fixed once drawn. |
labels | The categories: x axis points, bars or slices. |
series | [{ name, values }], one per line or bar group. Values are numbers, or null for a gap. |
title, subtitle | The caption. Optional. |
categoryLabel | The first column’s header in the table view. |
stacked, prefix, suffix, height | The same options as the data-* attributes. |
update() merges, so leave out anything that hasn’t changed. Jewel.chart.update(figure, data) works on any chart, including ones written as an HTML table.
04 — Usage
Use it for
- The first column for categories; each further column is a series.
- Cells written as they should read (“42%”, “120,400”). The hover card shows your text.
- A title on every chart that says what it shows.
Avoid
- More than five series. The sixth turns gray; fold extras into Other or split the chart.
- Series colors on text.
- Pie charts with more than six slices.
05 — Accessibility
Notes
- Every chart keeps its table behind a “Show data” link, and the table is all that shows without a script.
- Charts can be tabbed to; arrows step through points and a live region reads each one.
- The palette is checked for color-blind contrast, and lines are labeled at their ends where they don’t collide.