Chart
A light theme wrapper around TanStack Charts — the grammar and rendering are theirs, the light/dark chrome, tooltip surface, and categorical palette are the kit's.
Chart wraps TanStack Charts' own <Chart> (@tanstack/charts/react) — a typed, tree-shakable grammar for SVG charts. TanStack owns everything about building a chart: defineChart, marks (lineY, barY, areaY, …), scales, transforms, interactions, and the actual rendering. This component only makes the result look native to the kit — light/dark chrome, a themed DOM tooltip, and an optional categorical palette — so you build charts with TanStack's real API and never learn a second one.
@tanstack/charts is an optional peer dependency — install it yourself (npm install @tanstack/charts) alongside @42-central/ui-react, the same way DataTable works with @tanstack/react-table. It's still pre-1.0 (alpha) as of this writing: the TanStack Charts docs are the source of truth for its own API, and it's worth pinning an exact version.import { barY, defineChart } from '@tanstack/charts';
import { scaleBand } from '@tanstack/charts/scales/band';
import { scaleLinear } from '@tanstack/charts/scales/linear';
import { tooltip } from '@tanstack/charts/tooltip';
import { Chart } from '@42-central/ui-react/chart';
import '@42-central/ui-react/chart/styles.css';
const data = [
{ month: 'Apr', commits: 142 },
{ month: 'May', commits: 187 },
{ month: 'Jun', commits: 165 },
];
const definition = defineChart({
marks: [barY(data, { x: 'month', y: 'commits', inset: 2 })],
scales: {
x: { scale: () => scaleBand().padding(0.24) },
y: { scale: scaleLinear, nice: true, grid: true, axis: { label: 'Commits' } },
},
tooltip,
});
<Chart definition={definition} ariaLabel="Commits per month" height={280} />@42-central/ui-react/chart/styles.css is a separate, opt-in stylesheet — not part of the universal styles.css/theme.css/colors.css chain every consumer already imports. Only apps that render a Chart need TanStack's tooltip/chrome variables mapped to the kit's tokens, so it ships on its own.Chart type variety — magnitude, trend, relationship, distribution, composition — lives on chart types; wiring a chart's own interactions (click-to-select, an animated hover marker) lives on interactions. This page covers what Chart itself adds: theming and its own prop API.
Theming
Chrome — axis, grid lines, the frame, the legend, the crosshair — already renders through TanStack's own currentColor; Chart sets color on its root so it tracks the kit's [data-theme] toggle instead of the OS prefers-color-scheme TanStack's un-themed default would otherwise follow. The DOM tooltip's surface, border, shadow, and font theme through TanStack's own --ts-chart-tooltip-* custom properties, matched to the kit's floating-panel surface (the same brand-tinted recipe Popover uses).
Series color
A one-series chart paints with its palette's chart fill. color sets data-color on the chart's root, so the fill is that palette's — neutral included, black in light mode and white in dark:
Code
<Chart definition={chart} ariaLabel="Commits per month" height={200} color="blue" />
<Chart definition={chart} ariaLabel="Commits per month" height={200} color="purple" />
<Chart definition={chart} ariaLabel="Commits per month" height={200} color="orange" />
<Chart definition={chart} ariaLabel="Commits per month" height={200} color="neutral" />Without color, it inherits the nearest ancestor's data-color instead, like a Button or a Progress would — and with no ancestor palette at all, it falls back to the kit's default, neutral:
Code
<div data-color="green">
<Chart definition={chart} ariaLabel="Commits per month" height={200} />
</div>The chart fill is not quite --c-solid, the variable a Button fills with. A button's label sits on its fill; a chart mark has nothing on it, so what has to read is the fill against the page. In light mode, most 500 solids fall under the 3:1 contrast that non-text graphics need, so in light mode chart fills use the 600 step. Yellow, whose solid is already 400, uses 700. In dark mode, the fill is the solid, unchanged. chart/styles.css holds the per-color map (--chart-<name>), and both the single-series fill (--chart-solid) and the colors slots read from it.
Only a chart with no color channel follows the inherited fill — one whose marks never map a field to color. A chart whose definition sets its own color options (a range, a scale), or that's built from a defineChart function, keeps TanStack's own resolution untouched.
colors themes a multi-series chart, one --ts-chart-N slot per entry — TanStack's own default categorical scale picks these up with zero changes to how a chart's definition builds its color channel (see Commits by contributor for the full working example):
<Chart definition={definition} ariaLabel="Commits by contributor" colors={['blue', 'green', 'red']} />chartColors(colors?) — exported alongside Chart — resolves an ordered Color[] (default CHART_PALETTE) to CSS values (var(--chart-<name>) for a kit color, so the light/dark step comes along), for building an explicit scale yourself:
import { chartColors, CHART_PALETTE } from '@42-central/ui-react/chart';
defineChart({
// ...
color: {
domain: ['Open', 'In progress', 'Closed'],
range: chartColors(['orange', 'blue', 'green']),
},
});CHART_PALETTE leads with blue/green/red/pink — the only 4-combination of this kit's own hues where every pair (not just adjacent ones) clears a colorblind-safety check. The remaining five hues (orange, purple, teal, yellow, cyan) are still available for a 5th+ series — pair them with a legend or direct labels, which every multi-series example on the chart types page already ships.
API
Prop
Type
Changelog
Magic: The Gathering example
A full DataTable integration against a real API (scryfall.com) — server-driven sorting, filtering, and global search synced to the URL, a row-detail Drawer, and real loading/error/retry states.
Chart types
A tour of TanStack Charts' grammar through the same Chart wrapper, grouped by the job the data needs done — magnitude, trend, relationship, distribution, composition, and comparison.