Usage
label that names it, categories once, and one entry in series per set of bars.
categories is the list of labels; every series has one value per category, in the same order. With several series, a category's bars sit side by side in input order. A value that is not finite draws no bar. Callbacks report datasetIndex (the series' position in series, hidden series included) and index (the category position).
A series needs a name because it is the legend entry, the tooltip row and the data table column. Labels, names and formatter results are copy or data: pass LocalizedMessage, or wrap data with toDisplayValue.
orientation is BarChartOrientation.Vertical (default) or Horizontal. Horizontal bars grow from the inline start, so they mirror in right-to-left. A category label is never cut with an ellipsis. In a vertical chart a label that does not fit its band wraps inside it; in a horizontal chart the label column is as wide as the longest label, up to half the chart, and a longer label wraps there. Bands narrower than 48px cannot hold wrapped text, so the axis thins labels instead: it draws the last category and every label before it that fits with a 5px gap. The tooltip title always carries the whole text.
A series' color is a ChartColor palette solid token such as ChartColor.PaletteBlueSolid. A series' colors gives one token per bar, in category order; a bar past the end of the list takes the series color. With neither, a series takes Chroma's categorical palette in order: blue, orange, emerald, pink, teal, yellow, violet, lime, red, sky, fuchsia, cyan, then indigo, amber, purple, rose, green, and the order cycles after 17.
palette changes where default colors come from: ChartPalette.Categorical (default), a harmony preset such as ChartPalette.HarmonySquare read from --color-harmony-1 to --color-harmony-8 (defined by a theme exported from Chroma with that preset), or an explicit list of colors. A palette color names a series, never a meaning.
showValueLabels writes each value at the end of its bar, in the subtle foreground, formatted by formatValue. Room for the labels is reserved for you, so there is no grace to set. formatValue also formats axis ticks, tooltip rows and the data table, so a unit suffix is one function that returns the number with its unit. showValueAxis paints tick labels (default false) and beginAtZero (default true) keeps zero on the axis. A negative value hangs from the baseline with its label past the low end.
showLegend paints a legend of buttons, one per series, at legendPosition (Top, Bottom, Start, End). Pressing one hides or shows the series and rescales the axis. Control it with hiddenSeries and onHiddenSeriesChange(hidden, details), or start it with defaultHiddenSeries. The tooltip is on by default. The tooltip sits at the inline end of its anchor and stays inside the plot: it flips to the start side, or slides along the block axis, when it would cross an edge. Format it with formatTooltipTitle(index, category) and formatTooltipRow({ datasetIndex, index, value, formattedValue, dataset, category }). A bar chart is read-only. The keyboard rule is in the accessibility notes.
The chart is 100% wide and tall and takes its size from its parent. There is no size prop. Give it a parent with a definite height, or a flex column (or grid) that sizes it: in a flex column the chart is a flex item that grows to fill the parent and shrinks to fit a max-block-size, so a column capped by max-block-size: 25dvh or held between a min-block-size and a max-block-size keeps the chart, its legend and its axes inside it. A wrapper between the column and the chart must itself be a flex column (display: flex; flex-direction: column; flex: 1 1 auto; min-block-size: 0): percentage heights do not resolve against a parent that only has a maximum, so a plain block wrapper lets the chart keep its aspect-ratio height and overflow the cap. In a parent with no height it takes an aspect ratio (2:1, or 1:1 for a pie) instead of collapsing. A parent with a height wins. It prints in its final state and theme with no code. A parent that re-renders with equal series and categories rebuilds nothing, so you do not need to memoize them or the callbacks.
Moving a call site is mechanical: rename props, move the category labels to one field, delete the props the type checker flags, and replace Chart.js callbacks with the typed formatters. The contract table in CONTRACT.md lists every prop, enum and export of the old package with its replacement and whether a codemod could do it. There is no adapter: the old config and plugins objects do not type-check, so the compiler points at every line.
What to do, in order:
@prepared911/ui/bar-chart (and its styles.css next to tokens.css).labels from the dataset to categories, once, wrapped with toDisplayValue if they are already-formatted data.dataLabel to name (give it a real name: it was "" in the fixture), data to values, and backgroundColor given as a list to colors. A single backgroundColor or borderColor is color.config.indexAxis with orientation, and datalabels plus valueLabelSuffix with showValueLabels and formatValue.responsive, maintainAspectRatio, valueAxisGrace, layout, labelSuffix, xAxisType and plugins.printBackground. Rename showYAxis to showValueAxis.plugins.tooltip.callbacks with formatTooltipTitle and formatTooltipRow.label, and delete any print code.src/components/line-chart/docs/usage.md, which migrates it.
Before:
colorPerCategory keeps working as it is: the entries may be bare custom property names. The muted label color, the 15% grace, the legend switch, the tooltip label callback and the print flag are gone because they are the defaults or formatValue. The second consumer, without the s suffix, drops formatValue; the default is the locale's number format. Every category label shows; none is skipped.
This fixture uses PieChart. See src/components/pie-chart/docs/usage.md.
Before: the chart was sized by its parent, but it needed responsive: true and maintainAspectRatio: false to fill it.
lazy(() => import("@prepared911/ui/bar-chart")) pulls in no other chart, and importing it has no side effect.
Before: a 187-line hook listened for beforeprint and afterprint, called Chart.getChart(canvas) for each canvas, switched off responsive, resized the chart, swapped each canvas for a PNG <img>, undid it afterwards, and set plugins.printBackground: true.
After: delete the hook and printBackground. The bars are SVG rectangles at percent geometry, so they print; the layout is CSS, so it fills the resized print container; the entrance animation is off in print; and the chart prints in its current theme.