Charts
Package: @zuilib/charts · 0.4.0
Charts for analytics platforms, built on Recharts and styled only through ZUI tokens. The package has two layers:
- A serializable spec. One
<Chart spec={spec} rows={rows} />component renders any of ten chart types from a plain-JSONChartSpec— type, series, axes, formats, stacking, reference lines, brush. Because the spec is JSON, it can live in a database, come from a server, or be produced by an end-user chart builder. Formatting is named by token ('currency:USD','compact','percent','date:short'); custom formatter functions travel as React props, never inside the spec. - Styled primitives.
ChartFrame,ChartTooltipPanel,ChartLegendListand themed default prop bags for hand-written Recharts composition when a spec is not enough.
Charts cross-filter data tables: inside a CrossFilterProvider, clicking a
bar, slice or legend chip filters a linked @zuilib/data-grid (and any
other chart), and grid selection echoes back as a highlight — without the
charts ever importing the grid. See cross-filtering.
Install
pnpm add @zuilib/charts
React and React DOM are the required peers. Recharts, primitives and tokens
install transitively. Install recharts directly only if your code imports it
for hand-written composition. Load the component and chart Tailwind entries
once; see the
components getting started.
@import "tailwindcss";
@import "@zuilib/primitives/tailwind.css";
@import "@zuilib/charts/tailwind.css";
Chart types
Line, area, bar (grouped, stacked, percent, horizontal), pie/donut, scatter/bubble, composed (bar + line + area), radar, radial bar, funnel and treemap — the whole gallery with live specs is on the spec page.
Theming
Series colors come from eight new tokens, --chart-1 … --chart-8 — a
categorical palette validated for color-vision-deficiency separation in
both light and dark mode. Slots are assigned to series in fixed
declaration order and never cycled; the order is the CVD mechanism, so a
ninth series should be folded into "Other", not given a new hue.
A brand theme may override --chart-1 to its primary hue. Overriding more
slots requires re-validating the full order (adjacent pairs must keep
ΔE ≥ 8 under CVD simulation); an unvalidated reshuffle can silently make
two neighboring series indistinguishable for a colorblind reader.
Everything else — grid lines (--border), tick labels
(--muted-foreground), the tooltip panel (--popover), skeleton and empty
states — reads the same tokens as the rest of ZUI, so themes and dark mode
apply with no chart-specific work.
Small screens and touch
A chart fills its container. Leave width and height out and the height
follows aspect (16 / 9) with a 200px floor, so a phone-width chart keeps a
readable plot; pass height to fix the height and keep the width fluid.
ChartFrame is a CSS size container (@container, min-w-0): it never
holds a flex or grid parent open at the width of a previous render, so it
can sit in any column without a min-w-0 of your own.
Under 384px the value axis sizes to its labels and the category axis keeps only its end ticks; the legend wraps and, on touch pointers, its buttons grow to 44px hit areas. Desktop rendering is unchanged.
Accessibility
- The container is a
figurewith an accessible name (spec.titleorariaLabel); Recharts' keyboardaccessibilityLayerstays on. - The legend is real buttons (
aria-pressed, focus ring): click isolates a series, click again restores all, shift-click toggles one. Hidden entries dim but never disappear, so color stays attached to the entity. - Values in tooltips and labels wear text tokens, never the series color.
- Mark entrance animation is disabled automatically under
prefers-reduced-motion(and controllable via theanimateprop). - Three of the light-mode palette slots sit below 3:1 against white by
design (they must stay distinguishable from each other first); tooltips,
the interactive legend and a paired table view are the relief. When a
chart carries load-bearing values, keep a table view nearby — which is
exactly what the cross-filter wiring to
@zuilib/data-gridgives you.