Technical Documentation: chartist-js/chartist
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
Chartist
Simple, responsive charts, drawn in SVG and styled with CSS. Tiny, typed, and happy without a build step.
chartist.dev · Getting started · API · Examples · Plugins · Changelog
Coming from Chartist 1? Your code keeps working. Coming from 1.x shows what's new, and the 1.x docs are at chartist.dev/v1/.
Install
npm install chartistimport { lineChart } from 'chartist';
import 'chartist/styles.css';
lineChart('#chart', {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
series: [
[12, 9, 7, 8, 5],
[2, 1, 3.5, 7, 3]
]
});<div id="chart" class="ct-golden-section"></div>No bundler? Chartist is a native ES module:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/chartist@2/dist/index.min.css"
/>
<div id="chart" class="ct-golden-section"></div>
<script type="module">
import { lineChart } from 'https://cdn.jsdelivr.net/npm/chartist@2/dist/index.min.js';
lineChart('#chart', { series: [[1, 3, 2, 5]] });
</script>Why Chartist
- Style it with CSS. Plain SVG with predictable classes and custom properties for colors, lines and labels. No theme engine.
- Responsive, two ways. Options that change with media queries, or with the chart's own container.
- Composed charts. Bars and lines in one chart, on as many axes as you need.
- Renders anywhere SVG does. Render on a server, with real SVG text labels, for PDFs, email and images.
- Extend everything. Every element passes through an open pipeline you can hook into, and the community's plugins keep working.
- Tiny and typed. Written in TypeScript, no dependencies, and you ship only what you import.
Coming from 1.x
Your 1.x code keeps working: the chart classes draw what they drew before. 1.x events and plugins work with chartist/compat. The classes are deprecated, and 3.0 removes them from chartist. Coming from 1.x explains what changed and how to move to the functions.
Community wrappers
Chartist needs no wrapper: a chart is a container and a function call, and Charts in frameworks has recipes for React, Vue, Angular and Svelte, with server rendering. If you'd rather use a component, the community wraps Chartist for these stacks. Thank you to everyone who built them!
| Stack | Wrapper | Built for |
|---|---|---|
| React | react-chartist | Chartist 1.x |
| Angular | ng-chartist | Chartist 1.x |
| Vue | vue-chartist | Chartist 0.x |
| Node.js | node-chartist | Chartist 0.x |
| Ember | ember-cli-chartist | Chartist 0.x |
| Java (JSF) | ChartistJSF | Chartist 0.x |
| Vue 2 | v-chartist | Chartist 0.x |
| R | chartist (htmlwidget) | Chartist 0.x |
| Meteor | mfpierre:chartist-js | Chartist 0.x |
| WordPress | TablePress Extension: Chartist | Chartist 0.x |
| Blazor | UnlockedData.Chartist.Blazor | Chartist 0.x |
| Angular | ngx-chartist | Chartist 0.x |
| Ruby on Rails | chartist-rails | Chartist 0.x |
Wrappers built for 1.x run on v2: see Community wrappers for the npm overrides entry they need until they allow chartist@2. Maintain a wrapper that's missing here? Send a pull request.
Supported by
Cube sponsors Chartist's maintenance. Thank you!
Contributing
This repository holds the library (packages/chartist), its docs site at chartist.dev (apps/docs), the examples and the playground, in an Nx and pnpm monorepo. Clone it, run pnpm install, and run tasks through Nx, for example pnpm nx test chartist or pnpm nx dev docs. CONTRIBUTING.md has the setup, the tests and the submission guidelines.
License
Dual-licensed under MIT or WTFPL, at your choice.
Overview (`docs/visual-design/README.md`)
Visual design
Chartist's brand and the docs site's design were made in Claude Design. This folder is a snapshot of that canvas, kept as the design reference for the site.
- Canvas: https://claude.ai/artifact/CFCX75HRhyT28hXYSNdgbU (snapshot of version
1791919724-153a) - Implemented in
apps/docs: the tokens insrc/styles/tokens.css, the mascot insrc/components/Mascot.astro, the logomark insrc/components/Logomark.astro. Styling rules:apps/docs/STYLING.md.
To change the design, change the site; the canvas is the reference for new pages and components, not a source the site is generated from. When the design itself changes in the canvas, download a new snapshot here and say what changed in the commit.
Contents
project/canvas.json: the canvas index (artboard titles, positions, grouping).project/chartist.css: the design system. It holds the palette (--p-*), the semantic tokens for light (.cx) and dark (.cx.dark), the mascot colors (--m-*), the chart series palette (--ct-series-*, including the accessible theme), and component styles.project/*.dc.html: one file per artboard.project/paper.png,project/paper-dark.png: the design's paper texture, "Cream Paper" from Subtle Patterns (CC BY-SA 3.0). The site uses it as the page ground texture (apps/docs/src/assets/).assets/reference-*-0x.svg: the original 2014 mascot and icon, uploaded as a reference for the redesign.
The .dc.html files are Claude Design component pages. They need the Design runtime (support.js, not included) to render, so read them as source (markup, tokens, copy, data), and open the canvas link to see them rendered.
| Group | Artboards |
|---|---|
| Brand | Main (overview and rationale), MascotPoses, Logo |
| Brand system | Color, Typography, VoiceMotion, Tokens (tokens → chart theme), OgCard |
| Components (Astro islands) | Mascot, Logomark, Chart, LiveExample, SiteHeader, SiteFooter, ComparisonTable, ScrollStage |
| Docs pages (desktop) | Landing (scroll story), LandingStory (storyboard), GettingStarted, Examples, Playground, Api, Plugins, NotFound |
| Dark mode and mobile | LandingDark, MobileLanding, MobileDocs, MobilePlayground, MobileNotFound |
The design's key choices
- Mascot: the 0.x gentleman (top hat, moustache, red waistcoat, easel, hay fork), redrawn. All his colors are custom properties.
- Palette: cream became Paper, red became Waistcoat (
#C8102E), yellow became Mustard (logo tile), warm gray became Ink. - Chart series colors: unchanged from 1.5 (
--ct-series-a…-o), plus an opt-in color-blind-safe theme. - Typography: Zilla Slab (display) and Atkinson Hyperlegible Next (body).
Where the site differs from the snapshot
- Code font: JetBrains Mono instead of Atkinson Hyperlegible Mono. All fonts are self-hosted.
- Code samples use the functional API and the real export paths; the design's samples use the 1.x classes and old paths.
- Charts are drawn by Chartist. The design's
Chartcomponent draws them with its own simplified math, as a mockup. - Numbers and comparisons on the landing page come from a dated facts snapshot (
pnpm nx run docs:facts) and measurements, where the design has placeholders. The design's "Trusted by" logo row became a curated "Used by" list that links to the evidence.
Overview (`packages/chartist/README.md`)
Chartist
Simple, responsive charts, drawn in SVG and styled with CSS. Tiny, typed, and happy without a build step.
chartist.dev · Getting started · API · Examples · Plugins · Changelog
Coming from Chartist 1? Your code keeps working. Coming from 1.x shows what's new, and the 1.x docs are at chartist.dev/v1/.
Install
npm install chartistimport { lineChart } from 'chartist';
import 'chartist/styles.css';
lineChart('#chart', {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
series: [
[12, 9, 7, 8, 5],
[2, 1, 3.5, 7, 3]
]
});<div id="chart" class="ct-golden-section"></div>No bundler? Chartist is a native ES module:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/chartist@2/dist/index.min.css"
/>
<div id="chart" class="ct-golden-section"></div>
<script type="module">
import { lineChart } from 'https://cdn.jsdelivr.net/npm/chartist@2/dist/index.min.js';
lineChart('#chart', { series: [[1, 3, 2, 5]] });
</script>Why Chartist
- Style it with CSS. Plain SVG with predictable classes and custom properties for colors, lines and labels. No theme engine.
- Responsive, two ways. Options that change with media queries, or with the chart's own container.
- Composed charts. Bars and lines in one chart, on as many axes as you need.
- Renders anywhere SVG does. Render on a server, with real SVG text labels, for PDFs, email and images.
- Extend everything. Every element passes through an open pipeline you can hook into, and the community's plugins keep working.
- Tiny and typed. Written in TypeScript, no dependencies, and you ship only what you import.
Coming from 1.x
Your 1.x code keeps working: the chart classes draw what they drew before. 1.x events and plugins work with chartist/compat. The classes are deprecated, and 3.0 removes them from chartist. Coming from 1.x explains what changed and how to move to the functions.
Community wrappers
Chartist needs no wrapper: a chart is a container and a function call, and Charts in frameworks has recipes for React, Vue, Angular and Svelte, with server rendering. If you'd rather use a component, the community wraps Chartist for these stacks. Thank you to everyone who built them!
| Stack | Wrapper | Built for |
|---|---|---|
| React | react-chartist | Chartist 1.x |
| Angular | ng-chartist | Chartist 1.x |
| Vue | vue-chartist | Chartist 0.x |
| Node.js | node-chartist | Chartist 0.x |
| Ember | ember-cli-chartist | Chartist 0.x |
| Java (JSF) | ChartistJSF | Chartist 0.x |
| Vue 2 | v-chartist | Chartist 0.x |
| R | chartist (htmlwidget) | Chartist 0.x |
| Meteor | mfpierre:chartist-js | Chartist 0.x |
| WordPress | TablePress Extension: Chartist | Chartist 0.x |
| Blazor | UnlockedData.Chartist.Blazor | Chartist 0.x |
| Angular | ngx-chartist | Chartist 0.x |
| Ruby on Rails | chartist-rails | Chartist 0.x |
Wrappers built for 1.x run on v2: see Community wrappers for the npm overrides entry they need until they allow chartist@2. Maintain a wrapper that's missing here? Send a pull request.
Supported by
Cube sponsors Chartist's maintenance. Thank you!
License
Dual-licensed under MIT or WTFPL, at your choice.
Overview (`packages/essentials/README.md`)
@chartist-js/essentials
The Chartist additions people ask for most, as layers you add to a chart. They're kept out of the core, so charts that don't use them stay small, and they're made and released together with Chartist.
It holds axisTitle(), targetLine(), trendLine(), valueLabels() and logAxis() today. More layers and axes follow.
Install
npm install @chartist-js/essentialsIt depends on chartist, which npm installs with it. Load its stylesheet after Chartist's:
import 'chartist/styles.css';
import '@chartist-js/essentials/styles.css';Without a bundler
Map both packages in an import map, so the package and your page share one copy of Chartist:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/chartist@2/dist/index.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@chartist-js/essentials@2/dist/styles.css"
/>
<script type="importmap">
{
"imports": {
"chartist": "https://cdn.jsdelivr.net/npm/chartist@2/dist/index.min.js",
"@chartist-js/essentials": "https://cdn.jsdelivr.net/npm/@chartist-js/essentials@2/dist/index.js"
}
}
</script>
<script type="module">
import { lineChart } from 'chartist';
import { axisTitle } from '@chartist-js/essentials';
</script>Axis titles
axisTitle() names an axis. It reserves the room the title needs next to the axis labels, so you don't have to guess a padding, and the title never overlaps the labels:
import { lineChart } from 'chartist';
import { axisTitle } from '@chartist-js/essentials';
lineChart(
'#chart',
{ labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'], series: [[12, 9, 7, 8, 5]] },
{
layers: {
above: [
axisTitle({ axis: 'x', text: 'Day' }),
axisTitle({ axis: 'y', text: 'Visits' })
]
}
}
);One layer draws one title. On the presets the axes are x and y; in a composed chart, use the ids you gave the axes:
import {
autoScaleAxis,
axisGrid,
axisLabels,
cartesianAxes,
chart,
line,
stepAxis
} from 'chartist';
import { axisTitle } from '@chartist-js/essentials';
chart('#chart', {
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr'],
series: [
[120, 150, 170, 210],
[135, 160, 190, 230]
]
},
axes: cartesianAxes([
{ id: 'month', side: 'bottom', type: stepAxis() },
{ id: 'eur', side: 'left', type: autoScaleAxis() },
{ id: 'usd', side: 'right', type: autoScaleAxis() }
]),
layers: [
axisGrid({ axes: ['month', 'eur'] }),
line({ series: [0], x: 'month', y: 'eur' }),
line({ series: [1], x: 'month', y: 'usd' }),
axisLabels(),
axisTitle({ axis: 'month', text: 'Month', align: 'end' }),
axisTitle({ axis: 'eur', text: 'EUR' }),
axisTitle({ axis: 'usd', text: 'USD', flip: true })
]
});| Option | What it does |
|---|---|
axis |
The id of the axis to name |
text |
The title; an empty text draws nothing |
align |
start, middle (default) or end along the axis; the start of a Y axis is its bottom |
flip |
Left and right titles read from top to bottom instead of from bottom to top |
gap |
Space between the title and the axis labels, in px (default 4) |
className |
Extra classes for the title |
classNames |
{ title } replaces the class ct-axis-title |
Titles on the same side stack outward in the order of layers.
Styling
The title is an SVG text with the classes ct-axis-title, ct-horizontal or ct-vertical, and ct-start or ct-end. It looks like the axis labels until you say otherwise:
.ct-axis-title {
--ct-axis-title-color: #555;
--ct-axis-title-size: 0.875rem;
font-weight: 600;
}Target and trend lines
targetLine() draws a line across the chart at a value, such as a goal or a limit, with an optional label. trendLine() fits a straight line through each series' values (least squares) and draws it in the series' color:
import { lineChart } from 'chartist';
import { targetLine, trendLine } from '@chartist-js/essentials';
lineChart(
'#chart',
{
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
series: [[620, 810, 740, 930, 880, 1020]]
},
{
low: 0,
layers: {
above: [targetLine({ value: 1000, label: 'Goal' }), trendLine()]
}
}
);A target line takes value, label, className, the axis it's on (a Y axis draws it across, an X axis up and down) and where its label sits. A trend line's node knows its slope, intercept and correlation. On a log axis it's fitted to the logarithms, so it's straight there. Style them with --ct-target-line-color, --ct-target-line-width, --ct-target-line-dasharray, --ct-trend-line-width and --ct-trend-line-dasharray.
Value labels
valueLabels() writes each value next to its point or bar: above points, past the ends of bars, and in the middle of stacked segments. Labels that don't fit their bar or would leave the chart move to where they fit:
import { barChart } from 'chartist';
import { valueLabels } from '@chartist-js/essentials';
barChart(
'#chart',
{ labels: ['Q1', 'Q2', 'Q3'], series: [[1250, 1830, 1470]] },
{
layers: {
above: [
valueLabels({
locale: 'en-US',
numberFormat: { style: 'currency', currency: 'USD' }
})
]
}
}
);format(item) gets the same item as the format of @chartist-js/tooltips: undefined keeps the default text, null draws no label, and \n starts a new line. pointPosition, barPosition and offset place the labels, roles and series pick what gets one. Style them with --ct-value-label-color, --ct-value-label-inside-color and --ct-value-label-size.
Log axis
logAxis() is an axis type for values that span several orders of magnitude. Each power of the base is the same distance from the next, so 100, 1,000 and 10,000 are evenly spaced:
import { lineChart } from 'chartist';
import { logAxis } from '@chartist-js/essentials';
lineChart(
'#chart',
{
labels: ['2019', '2020', '2021', '2022', '2023', '2024'],
series: [[120, 980, 4100, 36000, 210000, 850000]]
},
{
axisY: {
type: logAxis({ minorTicks: true }),
labelTransform: value => value.toLocaleString('en-US')
}
}
);| Option | What it does |
|---|---|
base |
The base of the logarithm, any number above 1 (default 10) |
minorTicks |
Grid lines without labels between the ticks, styled with --ct-grid-minor-* (default off) |
The axis's own options work as on the core's axes: low and high set the range exactly, ticks sets the ticks by hand, and labelTransform formats the labels. Zero and negative values have no place on a log scale: they're drawn at the start of the axis. Use null for a gap. A trendLine() on a log axis is fitted to the logarithms, so it's straight on the chart.
On the server
The layers and the log axis are part of the scene, so they render with chartist/server and hydrate like the rest of the chart:
import { lineChartConfig } from 'chartist';
import { renderToString, toScene } from 'chartist/server';
import { axisTitle } from '@chartist-js/essentials';
const config = lineChartConfig(
{ labels: ['Mon', 'Tue', 'Wed'], series: [[12, 9, 7]] },
{ layers: { above: [axisTitle({ axis: 'y', text: 'Visits' })] } }
);
console.log(renderToString(toScene(config, { width: 600, height: 300 })));@chartist-js/terminal draws them too, vertical titles in a column.
License
MIT or WTFPL, like Chartist. See chartist.dev for the guides.
Overview (`packages/examples/README.md`)
@chartist/examples
The Chartist examples, shared by the docs site, the live playground and the visual regression tests (packages/visual). Private, never published.
Example format
Each example is a folder laid out like an npm create vite vanilla TypeScript starter (minus package.json):
<type>/<name>/
index.html the chart container and <script type="module" src="/src/main.ts">
src/main.ts imports 'chartist/styles.css' and the chart from 'chartist'
src/style.css optional, imported from src/main.tsCopy a folder into a Vite app that has chartist installed and it runs unchanged. Examples contain only code a user would write for that chart:
- no harness, test or docs code;
- only erasable TypeScript syntax (no enums, namespaces, parameter properties or decorators), with type-only imports marked
type; - CSS imported from TypeScript (
chartist/styles.css, relative./style.css), and no Vite-specific features (import.meta.glob,?raw, path aliases); - seeded randomness (an inlined
mulberry32) instead ofMath.random(), so every render is identical.
src/main.ts uses the functional API (lineChart(), chart(), v2 plugins) and teaches v2: when JavaScript would only style an element, it uses the custom properties (--ct-bar-width, --ct-color, …) or a class and a rule in src/style.css.
Each example has an entry in the manifest, index.ts (title, a one-sentence description for the gallery card and the example's page, viewports, sizing, settle time). pnpm nx check examples keeps the manifest and the folders in sync.
Classic entries
Every example that came from 1.x also has src/main.classic.ts, its original 1.x code (classic: true in the manifest; compat: true when that code needs the compat plugin). Never edit a classic file, comments included: it's the 1.x original. The docs show it behind the example's v2 / 1.x switch.
The visual tests screenshot both entries against one baseline image, recorded from Chartist 1.5.0 with the classic code, which proves that the functions draw what the classes drew. When the two differ:
- Suspect the functional code first: an option in the wrong place, a different default, a node replaced at a different position, attributes in a different order.
- If v2 draws it differently on purpose, give the entry a reason in the manifest and a baseline from the local build:
ownBaselinewhen only the functional entry differs,localBaselinewhen v2 changes the chart for both APIs (the 1.5.0 run then skips it). - If it's a library bug, fix the library (with a GitHub issue labeled
v2), not the example.
New examples have no 1.x code (v2: true); their baselines come from the local build, and the 1.5.0 run skips them. Baselines are recorded only on the reference platform (see "Visual regression" in docs/decisions.md).
Commands
pnpm nx typecheck examples: type-checks every example in strict mode against the builtchartistdeclarations (buildschartistfirst).pnpm nx check examples: validates the manifest against the folders.pnpm nx run visual:test:visual: screenshots every example (seepackages/visual).
Coverage of the 1.x examples
The 32 1.x CodeSandbox examples (sandboxes/ at the v1.5.0 tag) moved here in the Vite format. Their history follows the files: git log --follow <type>/<name>/src/main.ts for the functional code, which was rewritten in place, and git log --follow -C on src/main.classic.ts, a copy of the 1.x code.
Every 1.x Storybook story (src/charts/*/*.stories.ts) maps to an example. A story was merged into a sandbox example only if its data, options and event handlers are identical (same values and same behavior). All other stories became test-only examples (docs: false), with these changes:
- the detached
rootelement became the#chartcontainer; faker(storyLineChart/Scatter) became an inlined seededmulberry32, still drawing whole numbers from 0 to 100;new Date()(storyPieChart/OverlappingLabels) became a fixed date;PieChart/GaugeDonutandPieChart/RelativeDonutWidthdraw a fixed 500×500 chart, so their container has no fixed height and the tests keep its size (size: 'viewport').
LineChart/PathAnimation skipped its animation in storyshots; line/path-animation animates and the tests screenshot its end state. The viewports column lists the story's storyshots viewports (iPad → tablet, iPhone X → mobile, iPhone X landscape → mobile-landscape); pnpm nx check examples asserts that this table matches the manifest.
| Story | Example | Viewports | Mapping |
|---|---|---|---|
| LineChart/Default | line/simple | default | merged |
| LineChart/AutoScale | line/auto-scale | default | test-only |
| LineChart/Labels | line/labels | default | test-only |
| LineChart/MultiSeries | line/multi-series | default | test-only |
| LineChart/Holes | line/data-holes | default | merged |
| LineChart/FilledHoles | line/data-fill-holes | default | merged |
| LineChart/OnlyWholeNumbers | line/only-integer | default | merged |
| LineChart/NoInterpolationWithHoles | line/no-interpolation-with-holes | default | test-only |
| LineChart/CardinalInterpolationWithHoles | line/cardinal-interpolation-with-holes | default | test-only |
| LineChart/MonotoneCubicInterpolationWithHoles | line/monotone-cubic-interpolation-with-holes | default | test-only |
| LineChart/SimpleInterpolationWithHoles | line/simple-interpolation-with-holes | default | test-only |
| LineChart/StepInterpolationWithHoles | line/step-interpolation-with-holes | default | test-only |
| LineChart/StepNoPostponeInterpolationWithHoles | line/step-no-postpone-interpolation-with-holes | default | test-only |
| LineChart/SeriesOverrides | line/series-override | default | merged |
| LineChart/ReverseData | line/reverse-data | default | test-only |
| LineChart/FullWidth | line/full-width | default | test-only |
| LineChart/Scatter | line/scatter | default | test-only (integer data, unlike line/scatter-random) |
| LineChart/Area | line/area | default | merged |
| LineChart/BiPolarArea | line/bipolar-area | default | merged |
| LineChart/CustomPoints | line/modify-drawing | default | merged |
| LineChart/PathAnimation | line/path-animation | default | merged |
| LineChart/ViewBox | line/view-box | default | test-only |
| BarChart/Default | bar/auto-scale | default | test-only |
| BarChart/BiPolar | bar/bi-polar-interpolated | default | merged |
| BarChart/Labels | bar/labels | default | test-only |
| BarChart/MultilineLabels | bar/multiline | default | merged |
| BarChart/LabelsPlacement | bar/label-position | default | merged |
| BarChart/MultiSeries | bar/multi-series | default | test-only |
| BarChart/DistributedSeries | bar/distributed-series | default | merged |
| BarChart/ReverseData | bar/reverse-data | default | test-only |
| BarChart/Stack | bar/stacked | default | merged |
| BarChart/Horizontal | bar/horizontal | default | merged |
| BarChart/Adaptive | bar/adaptive | default, tablet, mobile-landscape, mobile | test-only (hides labels, unlike bar/extreme-responsive's noop) |
| BarChart/OverlappingBarsOnMobile | bar/overlapping-bars-on-mobile | default, mobile | test-only (other data and breakpoints than bar/overlapping-bars) |
| BarChart/PeakCircles | bar/with-circle-modify-drawing | default | merged |
| BarChart/AccumulateRelativeStack | bar/stacked-accumulate-relative | default | merged |
| BarChart/ViewBox | bar/view-box | default | test-only |
| PieChart/Default | pie/fixed-size | default | test-only |
| PieChart/Labels | pie/labels | default | test-only |
| PieChart/OverlappingLabels | pie/overlapping-labels | default | test-only |
| PieChart/LabelInterpolation | pie/label-interpolation | default | test-only (fixed size, unlike pie/simple) |
| PieChart/StartAngle | pie/start-angle | default | test-only |
| PieChart/SmallSlices | pie/small-slices | default | test-only |
| PieChart/IgnoreEmptyValues | pie/ignore-empty-values | default | test-only |
| PieChart/Donut | pie/donut | default | test-only |
| PieChart/GaugeDonut | pie/gauge-donut | default | test-only (fixed size and padding, unlike pie/simple-gauge) |
| PieChart/RelativeDonutWidth | pie/relative-donut-width | default | test-only |
| PieChart/Solid | pie/donut-chart | default | merged |
| PieChart/ViewBox | pie/view-box | default | test-only |
Overview (`packages/playground/README.md`)
@chartist/playground
In-browser playground for Chartist example projects: an editor with file tabs and a live preview of a Vite-style example (index.html, src/main.ts, src/style.css), without a server. Private, not published. Spec: openspec/specs/example-playground/spec.md.
How it works:
- Strip TypeScript in the browser (sucrase by default, ts-blank-space optional).
- Turn CSS imports from TypeScript into
<link>/<style>tags. - Resolve relative imports to data-URL modules through an import map, which also maps
chartistto a configurable URL. - Render the result into an `` via
srcdoc. Errors and console output come back throughpostMessage.
import { createPlayground } from '@chartist/playground';
await createPlayground(document.querySelector('#playground')!, {
files: { 'index.html': '…', 'src/main.ts': '…' },
chartistUrl: '/lib/chartist/index.js', // must send Access-Control-Allow-Origin
stylesUrl: '/lib/chartist/index.css',
types: { url: '/lib/chartist/' }, // index.d.ts and compat.d.ts, optional
chartistVersion: '^1.5.0' // for "Open in StackBlitz"
});compile(files, options) is the DOM-free core, usable on its own.
With types, TypeScript files get type errors (underlined; Ctrl/Cmd+Shift+M lists them), completions (on ., while typing, Ctrl+Space) and the type and doc comment of a name on hover. The TypeScript language service runs in a worker (src/types/), one per page, which loads when a TypeScript editor first gets focus: about 1.3 MB compressed with the lib declarations, from the page's own origin. Type errors never stop the preview. The playground element gets data-types="ready" once types answer. CSS files complete the stylesheet's --ct-* properties.
Tasks
| Command | What it does |
|---|---|
pnpm nx dev playground |
Demo with the example fixtures in demo/examples (Vite dev server) |
pnpm nx build playground |
Static demo in dist/, base path /chartist/ |
pnpm nx preview playground |
Serves dist/ at http://localhost:4321/chartist/ (see below) |
pnpm nx test playground |
Unit tests of the compile pipeline (Vitest) |
pnpm nx run playground:test:e2e |
Playwright, Chromium + Firefox + WebKit, against the built demo |
pnpm nx run playground:measure |
Bundle sizes and transformer timings |
The preview uses the docs dev server's port; while that runs, serve the demo on another one with node test/e2e/serve.mjs (from packages/playground). The e2e tests use port 4331.
Browsers for the e2e test: pnpm --filter @chartist/playground exec playwright install chromium firefox webkit.
The demo URL takes ?example=&transformer=sucrase|ts-blank-space, and &types=off for plain editing.
"Open in StackBlitz" can only be checked by hand: it opens stackblitz.com in a new tab.
Overview (`packages/terminal/README.md`)
@chartist-js/terminal
Chartist charts in the terminal. Line, bar and pie charts with Chartist's own layout, labels and series colors, drawn in braille dots and block characters:
It renders the scene Chartist builds for any chart (toScene()), the same one the SVG renderers get. So a terminal chart takes the options you know from lineChart(), barChart() and pieChart(), and the same chart can also come out as text-art HTML.
There are two ways to use it: the ct-cli command, and an API for your own CLI tools.
Install
npm install @chartist-js/terminalIt depends on chartist, which npm installs with it. The command needs Node.js 22 or later; the API runs in any JavaScript runtime, browsers included.
The command
npx @chartist-js/terminal demoInstalled globally (npm install @chartist-js/terminal), the command is ct-cli. Give it a chart type and the series, each a comma-separated list of numbers:
ct-cli line 12,9,7,8,5 2,1,3.5,7,3 --labels Mon,Tue,Wed,Thu,Fri --names Visitors,Sign-ups --smooth --area
ct-cli bar 5,4,3,7 3,2,9,5 --labels Q1,Q2,Q3,Q4 --stack
ct-cli bar 5,3,8 --labels Bananas,Apples,Kiwis --horizontal
ct-cli pie 20,10,30 --labels Bananas,Apples,Kiwis --donutAn empty value is a hole: 1,,3.
Data from files and pipes
Without series arguments, the data comes from --data <file> or from stdin. JSON works as { "labels": [...], "series": [...] } or as a plain array:
echo '{"labels":["Bananas","Apples","Kiwis"],"series":[20,10,30]}' | ct-cli pie --donutAnything else is read as rows of values, one series per row, separated by commas, semicolons, tabs or spaces. A first row that isn't numbers is the labels, and a first column that isn't numbers is the series names:
printf ',Mon,Tue,Wed\nVisitors,3,5,4\nSign-ups,1,2,3\n' | ct-cli bar --stackOptions
| Option | What it does |
|---|---|
-d, --data <file> |
Reads the data from a file |
-l, --labels <a,b,c> |
Labels of the values, or of the slices |
-n, --names <a,b> |
Series names, shown in a legend |
-w, --width <columns> |
Width in columns; your terminal's width by default |
-h, --height <rows> |
Height in rows; 20 by default |
--low, --high <n> |
The value axis range |
--smooth |
Line: smooths the lines |
--area |
Line: fills the area below the lines |
--no-points |
Line: hides the points |
--stack |
Bar: stacks the series |
--horizontal |
Bar: draws horizontal bars |
--donut |
Pie: draws a donut |
--no-labels |
Pie: hides the slice labels |
--palette <#hex,…> |
Series colors, first series first |
--no-legend |
Leaves out the legend |
--color <mode> |
auto, truecolor, 256, 16 or none; auto by default |
--html |
Prints HTML instead of text |
--standalone |
With --html: a complete page, styles included |
--help, --version |
What you'd expect |
-h is the height. Help is --help.
Colors follow your terminal: 24-bit when COLORTERM says so, 256 colors otherwise, none when the output goes to a file or a pipe, when NO_COLOR is set or TERM is dumb. FORCE_COLOR (0 to 3) overrides that, and --color overrides everything.
In your CLI tool
lineChart(), barChart() and pieChart() take the data and options of Chartist's presets, plus a size in columns and rows, and return the chart as a string:
import { detectColorMode, legend, lineChart } from '@chartist-js/terminal';
const data = {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
series: [
{ name: 'Visitors', data: [12, 9, 7, 8, 5] },
{ name: 'Sign-ups', data: [2, 1, 3.5, 7, 3] }
]
};
const color = detectColorMode(process.stdout, process.env);
console.log(
lineChart(data, {
columns: 60,
rows: 14,
showArea: true,
lineSmooth: true,
color
})
);
console.log(legend(data, { columns: 60, color }));The Chartist options work as they do in the browser: stackBars, horizontalBars, low and high, axisY.labelTransform, donut and the others. The terminal options are:
columnsandrows: the size in cells, 80 × 20 by default;color:truecolor(default),256,16ornonefor plain text;palette: series colors as hex, first series first, Chartist's colors by default;format:text(default),htmlfor a string of HTML, orelementfor the same markup as apreelement.
detectColorMode(stream, env) picks the color mode the way the command does; pass it process.stdout and process.env.
Any chart, any scene
The chart functions are shortcuts. For a composed chart, or a scene a plugin changed, lay it out with terminalSize() and render it yourself:
import { barChartConfig, toScene } from 'chartist';
import { renderToTerminal, terminalSize } from '@chartist-js/terminal';
const scene = toScene(
barChartConfig({ labels: ['Q1', 'Q2', 'Q3'], series: [[5, 4, 3]] }),
terminalSize(40, 10)
);
console.log(renderToTerminal(scene, { color: 'none' }));A cell stands for 8 × 16 px, so a braille dot is 4 × 4 px and circles stay round; terminalSize() measures text one cell per character, so labels land on the character grid. Nodes a plugin adds are drawn by their element: line and path as lines, circle as a filled disc of its radius (a point when it's tiny), text as a label.
HTML output
renderToHtml(scene), or format: 'html', gives the same chart as HTML: a pre element with classes instead of colors, ct-series-a, ct-grid, ct-label and so on. In a browser, renderToElement(scene), or format: 'element', builds that pre element with DOM methods, so the page needs no innerHTML, also where a Content Security Policy requires Trusted Types. Load the stylesheet to color it:
import '@chartist-js/terminal/styles.css';
import { lineChart } from '@chartist-js/terminal';
document
.querySelector('#chart')
?.replaceChildren(lineChart({ series: [[1, 3, 2]] }, { format: 'element' }));Every character sits in a cell 0.6 × 1 em, and the stylesheet brings a small font of its own (embedded, under 5 kB) for the braille dots, the grid and the bar blocks, drawn to fill a cell edge to edge. Fonts on the reader's system rarely have all of those, so the text art looks the same in every browser. Letters and digits use a monospace font: JetBrains Mono when the page loads it, else the system's. The stylesheet reads Chartist's custom properties (--ct-series-a to --ct-series-o, --ct-grid-color, --ct-label-color), so your Chartist theme colors the text art too. ct-cli --html --standalone writes a complete page.
Fonts
In a terminal, braille and box-drawing characters need a font that has them. Most terminal fonts do; on Windows, Cascadia Mono works. If the dots look scattered or the grid has gaps, your font is missing them. HTML output brings its own font for them, so it doesn't depend on the reader's fonts. The stylesheet embeds it as a data: URL, so a page with a Content Security Policy needs font-src data:; without it, the browser falls back to the reader's fonts.
Limits
- Line, bar and pie charts, and the nodes listed above. Other layers draw what their nodes are made of, or nothing.
- Each character of a label is one cell, so wide characters (CJK, emoji) shift the columns after them.
- No animation, no interaction: it prints a chart and is done.
License
MIT or WTFPL, like Chartist. See chartist.dev for the guide.
Overview (`packages/tooltips/README.md`)
@chartist-js/tooltips
Tooltips for Chartist charts: hover a point, tap a bar or tab to a slice, and a tooltip shows its label, series and value. It works with the mouse, touch and the keyboard, stays on its value while the chart updates, and takes its looks from CSS.
Install
npm install @chartist-js/tooltipsIt brings chartist along. Load its stylesheet after Chartist's, and add the plugin to a chart:
import 'chartist/styles.css';
import '@chartist-js/tooltips/styles.css';
import { lineChart } from 'chartist';
import { tooltips } from '@chartist-js/tooltips';
lineChart(
'#chart',
{
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
series: [{ name: 'Visitors', data: [1200, 1500, 1350, 1600, 1100] }]
},
{ plugins: [tooltips()] }
);Without a bundler
The browser build imports nothing, so it works next to any copy of Chartist 2:
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/chartist@2/dist/index.css"
/>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@chartist-js/tooltips@2/dist/styles.css"
/>
<div id="chart"></div>
<script type="module">
import { lineChart } from 'https://cdn.jsdelivr.net/npm/chartist@2/dist/index.min.js';
import { tooltips } from 'https://cdn.jsdelivr.net/npm/@chartist-js/tooltips@2/dist/index.min.js';
lineChart('#chart', { series: [[5, 9, 7, 8, 5]] }, { plugins: [tooltips()] });
</script>Your own text
numberFormat sets how the default text writes values, as Intl.NumberFormat options, and format replaces the text. Return null for no tooltip:
import { barChart } from 'chartist';
import { tooltips } from '@chartist-js/tooltips';
barChart(
'#chart',
{ labels: ['Q1', 'Q2', 'Q3'], series: [[1250, 1830, 1470]] },
{
plugins: [
tooltips({ numberFormat: { style: 'currency', currency: 'CHF' } })
]
}
);
barChart(
'#other',
{ labels: ['Q1', 'Q2', 'Q3'], series: [[12, 18, 0]] },
{
plugins: [
tooltips({
format: item =>
item.value === 0
? null
: `${item.label}\n${String(item.value)} orders`
})
]
}
);Content is text, never HTML, so data can't put markup into the page. For markup or a framework's template, render fills the tooltip yourself:
import { lineChart } from 'chartist';
import { tooltips } from '@chartist-js/tooltips';
lineChart(
'#chart',
{ series: [[5, 9, 7]] },
{
plugins: [
tooltips({
render(content, item) {
const value = document.createElement('strong');
value.textContent = String(item.valueIndex);
content.append('Value ', value);
// Return a function to undo it, e.g. to unmount a component.
}
})
]
}
);Options
| Option | What it does |
|---|---|
roles |
The roles that get tooltips: point, bar and slice by default; any role works |
format |
The text of a tooltip; null for none |
render |
Fills the tooltip with your own content; may return a cleanup |
numberFormat |
Intl.NumberFormat options for the values |
position |
element (default) at the value, or pointer to follow the mouse |
keyboard |
One tab stop per chart and arrow keys between values; false to turn it off |
className |
Extra classes for the tooltip |
Styling
The tooltip is .ct-tooltip, with .ct-tooltip-title, .ct-tooltip-series and .ct-tooltip-value. Change its look with custom properties, on the page or a chart's container:
.my-chart {
--ct-tooltip-background: navy;
--ct-tooltip-color: white;
--ct-tooltip-hide-delay: 500ms;
}It shows in the top layer, so overflow: hidden and z-index don't cut it off, and it's placed with the CSSOM, so it works under a strict Content Security Policy.
License
MIT or WTFPL, like Chartist. See chartist.dev for the guides.
Overview (`apps/docs/public/v0/README.md`)
The 0.x docs
These files are the Chartist 0.11 documentation, formerly athttps://gionkunz.github.io/chartist-js/. Astro copies them to /v0/ of the
site as they are. The old URLs redirect to chartist.dev: pages without a#hash to their v2 counterpart, sections without one to the same place
here.
They are not edited by hand beyond the changes listed below. The 0.x
toolchain (Grunt, Assemble, Bower) isn't kept.
Where they come from
The gh-pages branch of gionkunz/chartist-js at c1e49e1 (August 2022),
the last build of the 0.x site. Copied: the six pages (index.html,getting-started.html, examples.html, api-documentation.html,plugins.html, apidoc/index.html) and the files they load
(scripts/all.js, styles/, images/, favicon.ico, apidoc/scripts/,apidoc/styles/).
Not copied, because no page loads them: bower_components/ (most of the
build's 19 MB), test-chart.html, doc-clean-pull.sh, 404.html androbots.txt.
What changed
- Removed the "Get awesome Dashboard Templates by Creative Tim" section
of the home page, its.affiliate-project*CSS and its three images on
S3; the Google Analytics snippet on every page; the version notice pop-up
added in 2022 (its markup, CSS and script); and the html5shiv script for
IE 8 inapidoc/index.html, whose host is gone. - Fonts are served from
fonts/instead of Google Fonts and cdnjs: the
WebFont loader snippet on each page is replaced by a link tofonts/fonts.css, which loads Oxygen 300/400/700 and Source Code Pro
400/700 (SIL OFL 1.1, latin subset from fontsource) and Font Awesome
4.1.0. Their licenses are next to them. With this, no page makes a request
to another host. - Added a note at the top of each page: "This is the archived
documentation of Chartist 0.11. The current docs are at chartist.dev." Its<style>sits next to it. apidoc/index.htmlshowsimages/chartist-icon.svginstead of a
Chartist Guy GIF onraw.github.comthat no longer exists.
Overview (`apps/docs/public/v1/README.md`)
The 1.x docs
These files are the Chartist 1.5.x documentation (formerly athttps://chartist.dev/), the static output of its Docusaurus website. Astro
copies them to /v1/ of the site as they are.
They are not edited by hand. The 1.x docs won't be updated again, so the
Docusaurus stack isn't kept; if they ever need to change, rebuild them as
below and replace this folder.
How they were built
Built once on 2026-10-14, from the v1.5.0 sources (website/ ofchartist-js/chartist at the v1.5.0 tag, plus the commit after it that
removed the analytics):
- Copy the 1.x repository to a scratch folder outside this repository.
- In
website/docusaurus.config.js:baseUrl: '/v1/',noIndex: false, and
noeditUrl(there is nothing to edit any more).urlstayshttps://chartist.dev;trailingSlashstaysfalse, because the
generated API pages link each other relatively (../#series). pnpm@7 install --frozen-lockfile --ignore-scripts(the 1.x lockfile is
pnpm 7's), thenBRANCH=v1.5.0 docusaurus buildinwebsite/with Node 22.BRANCHsets the tree of the CodeSandbox example embeds, so they show the
1.5.0 sandboxes.- Copy
website/build/here (there are no source maps).
Pages are .html files linked without the extension (/v1/api/basics), as
Docusaurus writes them with trailingSlash: false; the host servespage.html for /page, as GitHub Pages does.
Overview (`apps/docs/src/assets/README.md`)
Assets
paper.png,paper-dark.png: the page ground texture, from the design (docs/visual-design/project/). Based on "Cream Paper" from Subtle Patterns (Toptal), licensed under CC BY-SA 3.0, recolored as an overlay for the Chartist v2 design. These two files stay under CC BY-SA 3.0 and need visible attribution on the site.sponsors/cube-logo-light.svg,sponsors/cube-logo-dark.svg: Cube's logo for light and dark grounds, unchanged from Cube's brand kit (https://cube.dev/brand, downloaded 2026-10-14). Its rules: use the files as provided (no recoloring or effects), the matching file for each ground instead of inverting, clear space of at least the cube mark's width around the logo, and no implied partnership beyond the real one (Cube sponsors Chartist).
Apps Docs Src Content Docs Getting Started (`apps/docs/src/content/docs/getting-started.mdx`)
title: Getting started group: Start here order: 1
import Step from '../../components/docs/Step.astro';
import Callout from '../../components/docs/Callout.astro';
import LiveExample from '../../components/LiveExample.astro';
export const data = {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
series: [
[12, 9, 7, 8, 5],
[2, 1, 3.5, 7, 3]
]
};