Some charts are read for their numbers. A reader who needs to know that Q4 made 15, not “about 15”, shouldn’t have to hover. valueLabels() writes each value next to the point or bar that shows it: above points, past the ends of bars, below them for negative values, and in the middle of stacked segments.
It comes from @chartist-js/essentials, with its stylesheet after Chartist’s:
import 'chartist/styles.css';
import '@chartist-js/essentials/styles.css';
Labels on bars
Add it as an extra layer, above on a preset:
import 'chartist/styles.css';
import '@chartist-js/essentials/styles.css';
import './style.css';
import { barChart } from 'chartist';
import { valueLabels } from '@chartist-js/essentials';
// Profit per quarter in thousands: labels past the end of each bar, below
// it for a loss. `high` leaves room above the highest bar for its label.
barChart(
'#chart',
{
labels: ['Q1', 'Q2', 'Q3', 'Q4', 'Q5', 'Q6'],
series: [[12, -4, 8, 15, -2, 9]]
},
{
high: 18,
layers: {
above: [
valueLabels({
locale: 'en-US',
numberFormat: { signDisplay: 'exceptZero' }
})
]
}
}
);The layer reads the bars and points after the other layers drew them, so it follows stacking, seriesBarDistance, horizontal bars and anything a compose listener changed, without knowing their options. A bar a listener dropped gets no label.
Text
The default text is the value along the value axis, written with Intl.NumberFormat. numberFormat takes its options, and locale its locale:
import { valueLabels } from '@chartist-js/essentials';
export const money = valueLabels({
locale: 'de-CH',
numberFormat: { style: 'currency', currency: 'CHF' }
});
Set locale when charts render on a server: without it, the server’s locale writes the numbers.
For anything else, format(item) returns the text. It gets the same item as the format of @chartist-js/tooltips: the value, its meta, the series and its name, the label at the value’s index, and the node’s context. Return undefined to keep the default text, null or an empty string for no label, and \n for a second line. Here the text comes from each value’s meta:
import 'chartist/styles.css';
import '@chartist-js/essentials/styles.css';
import './style.css';
import { barChart } from 'chartist';
import { valueLabels } from '@chartist-js/essentials';
// Hours per work order on three machines, stacked: each segment says
// which order it is, from the value's meta.
const order = (value: number, meta: string) => ({ value, meta });
barChart(
'#chart',
{
labels: ['Press', 'Lathe', 'Mill'],
series: [
[order(14, 'WO 4182'), order(9, 'WO 4190'), order(11, 'WO 4201')],
[order(8, 'WO 4185'), order(12, 'WO 4193'), order(6, 'WO 4204')],
[order(5, 'WO 4187'), order(7, 'WO 4198'), order(10, 'WO 4207')]
]
},
{
horizontalBars: true,
stackBars: true,
axisY: { offset: 60 },
layers: {
above: [
valueLabels({
format: item => `${String(item.meta)}\n${String(item.value)} h`
})
]
}
}
);Holes get no label, and format isn’t called for them. A value of 0 is labeled.
Where they go
| Option | What it does |
|---|---|
pointPosition |
above (default), below, left, right or center of a point |
barPosition |
end (default), inside-end, center or inside-base, from the bar’s base to its value |
offset |
The gap in px between the point or the bar’s end and the label: 8 for points, 4 for bars |
roles |
['point'], ['bar'] or both (default) |
series |
The series to label, by name or index; default: all of them |
overflow |
fit (default) or show |
labels |
svg (default) or html |
Segments of stacked bars are labeled in their middle, since past a segment’s end is the next one. A line drawn without points (showPoint: false) gets its labels where the points would be.
When they don’t fit
With overflow: 'fit', a label that doesn’t fit inside its bar goes past its end instead; in a stacked segment, it’s left out. A label that would leave the chart moves inward, and a bar’s label that would leave it at the top goes inside the bar’s end. Bars are 10 px thin by default, so a label inside one is cramped: give the chart room instead, with a high above the highest value or more chartPadding, or make the bars wider with --ct-bar-width, as the examples do. overflow: 'show' draws every label where its position puts it.
Styling
Labels look like the axis labels until you say otherwise. Each one has the class ct-value-label and its series’ class, so one line gives them their series’ colors:
/* Each label in its series' color, set by its ct-series-* class. */
.ct-value-label {
--ct-value-label-color: var(--ct-color);
}| Property | What it sets |
|---|---|
--ct-value-label-color |
The text color; default: the axis label color, light and dark |
--ct-value-label-inside-color |
The color of labels inside a bar; default: white |
--ct-value-label-size |
The font size; default: the axis label size |
Labels inside or centered in a bar also have ct-value-label-inside. classNames replaces group (ct-value-labels), label and inside.
SVG, HTML, server and terminal
Labels are SVG text by default, whatever the chart’s labels option says, so they render the same on a server, in an exported image and in the terminal. labels: 'html' draws each one as a span in a foreignObject instead, for CSS boxes: a background, padding, rounded corners.
In listeners and plugins, find them by the role valueLabel. Its context has the labeled value, its indexes, the text and where the label ended up. They’re keyed like their values, so with live updates a label keeps its element and moves with its bar.
Coming from the label plugins
chartist-plugin-pointlabels and chartist-plugin-barlabels were 1.x draw listeners. valueLabels() covers both:
| Plugin option | With valueLabels() |
|---|---|
labelInterpolationFnc |
format, which gets the value and its meta, series and label |
labelOffset, textAnchor, align |
pointPosition, barPosition and offset |
labelClass |
classNames: { label }; the default is ct-value-label |
showZeroLabels |
Zero is labeled; return null from format to leave it out |
thresholdPercentage, startAtBase |
barPosition: 'inside-end' or 'inside-base', with fitting |
Add the layer to layers.above instead of plugins. It draws in g.ct-value-labels above all series, so a later series doesn’t cover an earlier series’ labels.