Chart
Bar, line, area and proportion charts in a color-blind aware categorical palette, with dashes and end markers on lines, direct labels, a tooltip on hover and focus, and a table for screen readers. No chart library.
On this page
Installation
Usage
import { Chart } from "@/components/ui/chatcn/chart"
<Chart
type="line"
title="p95 latency since the connection pool change"
data={rows}
xKey="day"
series={[{ key: "api", label: "API" }, { key: "edge", label: "Edge" }]}
unit="ms"
/>Data is an array of rows. xKey names the category field (default label) and series the value fields (default value). Values format with valueFormat, unit and currency, or your own format.
Examples
Bar, stacked, area and proportion
Horizontal bars suit long names and rankings and carry their value at the tip. Stacks separate segments with a 2 px gap in the surface color. The proportion bar replaces a pie: one bar, labelled rows with percentages, and nothing to compare by angle.
Streaming points
Pass expectedCount and domain while points stream in. Slots and the axis are reserved, so earlier points never move or rescale. Each new mark reveals once from its baseline.
Loading, empty and error
Encodings
| Series | Bars and areas | Lines |
|---|---|---|
| 1 | --chatcn-chart-1 (blue) | Blue, solid, circle |
| 2 | --chatcn-chart-2 (amber) | Amber, solid, square |
| 3 | --chatcn-chart-3 (teal) | Teal, dashed, diamond |
| 4 | --chatcn-chart-4 (magenta) | Magenta, dotted, triangle |
| 5 | --chatcn-chart-5 (orange) | Orange, solid, circle |
| 6 | --chatcn-chart-6 (violet) | Violet, dashed, square |
Lines label their ends directly when the labels have room and show a legend only when they would collide. Bars show a legend from two series up. highlight keeps one category at full strength and steps the rest back. The hues are picked to stay apart for the common color blindness types; override the six variables to use your brand palette.
Props
| Prop | Type | Notes |
|---|---|---|
type | "bar" | "line" | "area" | "proportion" | Default bar |
data | ChartDatum[] | Rows. Non-numeric values read as gaps |
xKey | string | Category field. Default label |
series | (string | { key, label? })[] | Up to six. Default ["value"] |
layout | "vertical" | "horizontal" | Bars only |
stacked | boolean | Stack bar series |
height | number | Plot height in px. Default 200 |
valueFormat, unit, currency, format | Value formatting. Ticks compact from 1,000 up | |
domain | [number, number] | Fixed value range, for streaming |
expectedCount | number | Slots reserved while streaming |
valueLabels | "auto" | "all" | "none" | Column cap labels. auto labels when all fit |
highlight | string | Category to keep in ink |
summary | string | Screen reader summary. Generated when omitted |
loading, streaming, error, empty | States | |
view, defaultView, onViewChange | "chart" | "table" | Controlled or uncontrolled table toggle |
variant | "card" | "plain" | Default card |
ProportionBar is exported on its own for meters and breakdowns.
Keyboard
| Key | Action |
|---|---|
Tab | Focus the plot (one stop), then the table toggle |
Arrow keys | Move between categories and show their values |
Home / End | First or last category |
Escape | Hide the tooltip |
Accessibility
- Every chart renders a real table: hidden visually by default, shown with the table toggle. The plot is described by it and labelled with a generated summary of range and peaks.
- Series never depend on telling grays apart: texture, dashes and marker shapes carry identity too, which also survives grayscale printing.
- The tooltip mirrors what the table holds, so it enhances and never gates.
Motion
- Marks reveal once with a clip-path from their baseline, 280 ms ease-out, lines draw left to right in 300 ms. Re-renders never replay; streamed points reveal on their own.
- The tooltip fades in once in 125 ms, then follows the pointer without animation. Keyboard navigation is instant.
- Reduced motion swaps every reveal for a 150 ms fade.