Rime

Chart

Themed charts on Recharts: line, area, bar and donut in one frame with a table view, plus sparklines and a stat strip.

The chart layer lives in components/charts. Every chart mounts in ChartFrame: a caption, a legend, the plot and a Chart or Table toggle whose table is the same data for anyone who cannot read the picture. Loading, refreshing, empty and error states are built in (status).

Charts carry no hex. Every mark reads a custom property (--chart-1 to --chart-6, --chart-seq-*, --chart-good and friends) declared in tokens.css, and chart chrome (grid, axes, ticks) derives from the global tokens, so a preset, dark mode or a Rime Studio theme re-themes them with no client code. Tooltips glide between points (TOOLTIP_MOTION: Recharts' 400ms ease, off under reduced motion) and use the compact shadcn layout on the popover glass, with dot, line or dashed indicators.

The high-level charts take series (Rime's list) or config (a shadcn ChartConfig), and expose --color-<key> per series on the root. For anything custom, the shadcn-compatible primitives are here too: ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent and type ChartConfig. A shadcn chart example works with its Tailwind classes removed and ChartContainer given a height. The full gallery, with every tooltip variant, is at /charts.

Series take their colour in a fixed order (the colour-blind safety mechanism): never re-rank on filter, and past six series fold the rest into "Other" (foldSeries). summary is required: one or two sentences a screen reader hears in place of the plot.

StatStrip is the summary-card pattern for figures: one surface, hairline cells, each with a value, a delta in words and an optional Sparkline.

Installation

npx shadcn@latest add KezLahd/rime/charts

Also installs: base, button, icon, skeleton, tabs.

Usage

import { AreaChart, BarChart, ChartContainer, ChartTooltip, ChartTooltipContent, DonutChart, LineChart, Sparkline, StatStrip, type ChartConfig } from "@/components/charts";

<LineChart
  title="Orders per month"
  summary="Online orders rose from 182 in January to 344 in August."
  data={rows}
  x="month"
  series={[{ key: "online", label: "Online" }]}
/>

Examples

Area, two series

Orders per monthOnline and retail, January to August 2026

Online orders rose every month, from 182 in January to 344 in August. Retail held between 118 and 161.

  • Online
  • Retail

Line with a reference

Online orders

Online orders rose from 182 in January to 344 in August, against a target of 300.

Bars, labelled

Revenue by regionYear to date

North leads with 48,200 in revenue; Central is lowest at 18,900.

Donut

Projects by status

Of 40 projects, 22 are on track, 9 in review, 6 drafts and 3 blocked.

Stat strip with sparklines

Orders
505
+32vs last monthMonthly, last 8 months: 302 to 505
Revenue
165,400
+12,800vs last month
Median turnaround
1.8days
−0.3vs last month
Open invoices
14
3 overdue

The shadcn primitives

ChartContainer with a ChartConfig, Recharts marks reading var(--color-<key>), and ChartTooltipContent. See /charts for every tooltip variant.

API

Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.

TrendChart

TrendChart props
PropTypeDefaultDescription
titleRequiredReactNode
subtitleReactNode
summaryRequiredstringOne or two sentences a screen reader hears in place of the picture: what is plotted and the headline ("Weekly orders rose from 8 to 32"). Also set as the SVG's <desc>.
hideTitlebooleanKeeps the title for assistive tech but hides it, when a Group heading already says it.
statusChartStatus
emptyTitleReactNode
emptyDescriptionReactNodeWhat to do next, or what will fill this. A bare "No data" is not an empty state.
emptyActionReactNode
errorTitleReactNode
errorDescriptionReactNode
onRetry() => void
actionsReactNodeControls placed beside the table toggle (a segmented control, a menu).
heightnumber248Plot height in px, x-axis band included.
classNamestring
dataRequiredReadonlyArray<ChartRow>
xRequiredstringField holding the x value (an ISO date for time series).
formatX(value: string) => stringStringAxis tick: short ("23 Nov").
formatXLong(value: string) => stringTooltip header and table rows: in full ("Week of 23 Nov 2026").
formatValue(value: number) => stringformatNumber
xLabelstring"Week"Table header for the x column.
endLabelsbooleantrueValue and name at each line's end. On by default for up to four series; switched off automatically where the ends would collide.
reference{ value: number; label: string }One horizontal limit on the same axis (program places). Never a second axis.
tooltipTagstringTooltip header tag, e.g. "Cumulative".
seriesRequiredReadonlyArray<ChartSeries> | never
confignever | ChartConfig

AreaChart

AreaChart props
PropTypeDefaultDescription
titleRequiredReactNode
subtitleReactNode
summaryRequiredstringOne or two sentences a screen reader hears in place of the picture: what is plotted and the headline ("Weekly orders rose from 8 to 32"). Also set as the SVG's <desc>.
hideTitlebooleanKeeps the title for assistive tech but hides it, when a Group heading already says it.
statusChartStatus
emptyTitleReactNode
emptyDescriptionReactNodeWhat to do next, or what will fill this. A bare "No data" is not an empty state.
emptyActionReactNode
errorTitleReactNode
errorDescriptionReactNode
onRetry() => void
actionsReactNodeControls placed beside the table toggle (a segmented control, a menu).
heightnumberPlot height in px, x-axis band included.
classNamestring
dataRequiredReadonlyArray<ChartRow>
xRequiredstringField holding the x value (an ISO date for time series).
formatX(value: string) => stringAxis tick: short ("23 Nov").
formatXLong(value: string) => stringTooltip header and table rows: in full ("Week of 23 Nov 2026").
formatValue(value: number) => string
xLabelstringTable header for the x column.
endLabelsbooleanValue and name at each line's end. On by default for up to four series; switched off automatically where the ends would collide.
reference{ value: number; label: string }One horizontal limit on the same axis (program places). Never a second axis.
tooltipTagstringTooltip header tag, e.g. "Cumulative".
seriesRequiredReadonlyArray<ChartSeries> | never
confignever | ChartConfig
stackedbooleanParts of a whole over time. Off for a single running total.

BarChart

BarChart props
PropTypeDefaultDescription
titleRequiredReactNode
subtitleReactNode
summaryRequiredstringOne or two sentences a screen reader hears in place of the picture: what is plotted and the headline ("Weekly orders rose from 8 to 32"). Also set as the SVG's <desc>.
hideTitlebooleanKeeps the title for assistive tech but hides it, when a Group heading already says it.
statusChartStatus
emptyTitleReactNode
emptyDescriptionReactNodeWhat to do next, or what will fill this. A bare "No data" is not an empty state.
emptyActionReactNode
errorTitleReactNode
errorDescriptionReactNode
onRetry() => void
actionsReactNodeControls placed beside the table toggle (a segmented control, a menu).
heightnumberPlot height in px, x-axis band included.
classNamestring
dataRequiredReadonlyArray<ChartRow>
categoryRequiredstringField holding the category (region, team member, week, stage).
orientation"columns" | "bars" | "columns" | "bars""columns"columns = vertical bars, for time and short labels. bars = horizontal, for many or long-named categories (regions, team members).
stackedbooleanfalseParts of a whole per category.
categoryColorsReadonlyArray<string>One colour per category, for ordered categories on a single series (a funnel's stages, from ordinalColors). Nominal categories never get this: one series is one colour.
formatCategory(value: string) => stringString
formatCategoryLong(value: string) => string
formatValue(value: number) => stringformatNumber
categoryLabelstring"Category"
valueLabelsbooleanValue at each bar's end. Default: on for a single unstacked series.
maxnumberFixed value-axis top, e.g. 5 for places per team member.
categoryWidthnumber150Width reserved for horizontal-bar category labels.
tooltipTagstring
seriesRequiredReadonlyArray<ChartSeries> | never
confignever | ChartConfig

DonutChart

DonutChart props
PropTypeDefaultDescription
summaryRequiredstringOne or two sentences a screen reader hears in place of the picture: what is plotted and the headline ("Weekly orders rose from 8 to 32"). Also set as the SVG's <desc>.
titleRequiredReactNode
classNamestring
subtitleReactNode
hideTitlebooleanKeeps the title for assistive tech but hides it, when a Group heading already says it.
statusChartStatus
emptyTitleReactNode
emptyDescriptionReactNodeWhat to do next, or what will fill this. A bare "No data" is not an empty state.
emptyActionReactNode
errorTitleReactNode
errorDescriptionReactNode
onRetry() => void
actionsReactNodeControls placed beside the table toggle (a segmented control, a menu).
dataRequiredReadonlyArray<DonutDatum>
centreLabelRequiredstringCentre figure label, e.g. "Customers". The figure is the total.
centreValuestringOverride the centre figure (defaults to the sum).
formatValue(value: number) => stringformatNumber
sizenumber168Ring diameter in px.
categoryLabelstring"Group"

StatStrip

StatStrip props
PropTypeDefaultDescription
statsRequiredReadonlyArray<Stat>
loadingboolean
classNamestring
aria-labelstring

Sparkline

Sparkline props
PropTypeDefaultDescription
valuesRequiredReadonlyArray<number>
widthnumber96
heightnumber28
emphasiseLastbooleantrueThe history in the de-emphasis hue, the current period (last segment and end dot) in the accent. Set false for an all-accent line.
colorstring"var(--chart-1)"Accent colour, a var() from palette.ts.
zeroBasedbooleantrueInclude zero in the scale. On for counts, off for rates that never near it.
classNamestring

ChartContainer

ChartContainer props
PropTypeDefaultDescription
configRequiredChartConfig
childrenRequiredComponentProps<typeof ResponsiveContainer>["children"]One Recharts chart (AreaChart, BarChart...).
initialDimension{ width: number; height: number }{ width: 320, height: 200 }Size used before the first measure, so SSR and first paint are not 0 by 0.

Also accepts the native attributes of AriaAttributes, DOMAttributes, HTMLAttributes.

ChartTooltipContent

ChartTooltipContent props
PropTypeDefaultDescription
classNamestring
indicatorIndicator"dot"dot (default), line or dashed key beside each row.
hideLabelbooleanfalse
hideIndicatorbooleanfalse
labelKeystringConfig key to read the label from, when the label is not the x value.
nameKeystringRow name from this field of the datum (pies: "browser").
colorstringForces every indicator to one colour.
labelFormatter(label: ReactNode, payload: ReadonlyArray<TooltipPayloadEntry>) => ReactNodeHeader text. Receives the resolved label and the payload.
formatter( value: TooltipPayloadEntry["value"], name: TooltipPayloadEntry["name"], item: TooltipPayloadEntry, index: number, datum: unknown, ) => ReactNodeReplaces a row's whole body.
valueFormatter(value: number) => stringformatNumberFormats each numeric value (kit default: formatNumber).
tagstringSmall uppercase tag beside the label ("Cumulative"). Kit extra.
totalboolean | stringAdds a total row: stacked charts. Kit extra.

Also accepts the native attributes of DOM attributes, Props.

ChartLegendContent

ChartLegendContent props
PropTypeDefaultDescription
classNamestring
hideIconbooleanfalse
nameKeystring
payloadReadonlyArray<LegendPayload>
verticalAlign"top" | "middle" | "bottom""bottom"

Theme tokens

Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.

Chart per-component tokens
TokenDefaultLive value
--chart-axisvar(--line)…
--chart-bandvar(--wash-hover)…
--chart-cursorvar(--line)…
--chart-gridvar(--line-soft)…
--chart-tickvar(--ink-muted)…
--popover-blurnone…
--popover-bordertransparent…
--popover-shadowvar(--shadow-float)…
--popover-surfacevar(--surface-solid)…
--r-circle50%…
--r-flat2px…
--stat-count4…

System tokens it reads

Chart system tokens
TokenDefaultLive value
--brandtokens.css…
--danger-filltokens.css…
--danger-inktokens.css…
--durtokens.css…
--dur-slowtokens.css…
--easetokens.css…
--focus-ringtokens.css…
--font-bodytokens.css…
--glass-fill-nestedtokens.css…
--indicatortokens.css…
--ink-bodytokens.css…
--ink-brandtokens.css…
--ink-disabledtokens.css…
--ink-headingtokens.css…
--ink-mutedtokens.css…
--ink-secondarytokens.css…
--linetokens.css…
--line-glasstokens.css…
--line-softtokens.css…
--r-2xstokens.css…
--r-mdtokens.css…
--r-smtokens.css…
--r-xstokens.css…
--rgb-brandtokens.css…
--rgb-brand-deeptokens.css…
--shadow-floattokens.css…
--success-inktokens.css…
--surface-solidtokens.css…
--surface-sunkentokens.css…
--table-cell-padtokens.css…
--table-head-filltokens.css…
--table-head-inktokens.css…
--table-head-padtokens.css…
--table-head-ruletokens.css…
--table-row-hovertokens.css…
--table-row-linetokens.css…
--text-captiontokens.css…
--text-microtokens.css…
--text-smalltokens.css…
--tracking-labeltokens.css…
--wash-activetokens.css…
--wash-hovertokens.css…
--wash-subtletokens.css…

Accessibility

  • Every chart is a <figure> labelled by its title and described by summary, which is also the SVG's <desc>.
  • The Chart or Table toggle shows the same data as a real table with a caption, for screen readers and for anyone comparing values.
  • Colour is never the only key: legends name each series, end labels sit on lines, status hues always come with a label.
  • The categorical order is the colour-blind safety mechanism; keep it.

Do and don't

Do

  • Write summary as the headline: what changed and by how much.
  • Use StatStrip for headline figures: one surface, hairline cells, never a grid of separate cards.
  • Pass status="loading" while data loads so the skeleton matches the chart's shape.

Don't

  • Don't hard-code a hex on a mark; set --chart-* tokens instead.
  • Don't use more than six series; fold the rest into Other.
  • Don't use status colours (good, warning, danger) as series identity.

For AI agents

This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.

Open chart.md

Related: meter, table