Installation
pnpm dlx shadcn@latest add https://dithercn.dev/r/line-chart.jsonUsage
import { LineChart } from "@/components/dithercn/line-chart";
const data = [
{ time: "12:00", p10: 160, p50: 188, p90: 238 },
{ time: "12:30", p10: 164, p50: 184, p90: 230 },
{ time: "13:00", p10: 167, p50: 191, p90: 245 },
];
export function Latency() {
return (
<LineChart
data={data}
x="time"
series={[
{ label: "p50", value: "p50", range: { low: "p10", high: "p90" } },
]}
/>
);
}Each series is a 2px line. Give it a range and the space between low and high fills with a light dither, which suits percentiles, forecasts and min/max envelopes. The readout lists the value with its range.
Examples
This week against last week
A dashed series reads as a reference: a previous period or a target. The legend repeats the dash, so the two lines never rely on colour alone. Here this week's range runs from last week's value to its own, so the dither fills exactly the gap between the two.
Few points, every one marked
With sparse data, markers="all" puts a dot on each point and curve="linear" keeps straight segments between them, so nobody reads a curve the data does not have. The band is a sector benchmark in its own color, and zero starts the axis at 0 because a score's distance from zero matters here.
Props
| Prop | Type | Default |
|---|---|---|
| data* One entry per point along the x axis, in order. | Type: readonly T[] | Default – |
| x* The field (or function) holding each point's x axis label. | Type: keyof T | ((datum: T) => string | number) | Default – |
| series* Each series is { label, value, color?, range?: { low, high, color? }, dashed? }. value, low and high are numeric fields of T or functions. | Type: [LineSeries<T>, ...LineSeries<T>[]] | Default – |
| markers Which points get a dot. | Type: "all" | "last" | "none" | Default "last" |
| zero Start the value axis at zero. By default it fits the data, so small changes stay visible. | Type: boolean | Default false |
| height Height in px, axes included. | Type: number | Default 240 |
| curve smooth is a monotone curve that never overshoots a data point. | Type: "smooth" | "linear" | Default "smooth" |
| fill Texture of the range bands. gradient is one even dither level, set by level; solid draws a translucent band. | Type: "gradient" | "dotted" | "hatched" | "solid" | Default "gradient" |
| bloom A soft glow in each series' colour behind its fill. Its strength follows --dither-bloom, lower in light mode. | Type: "off" | "low" | "high" | Default "off" |
| colors Colours by series position: palette names such as "green" or any CSS colour. A series' own color wins. | Type: readonly DitherColor[] | Default ink, orange, green, pink, … |
| interactive Scrubbing, the readout and the legend spotlight. false makes a decorative chart: no focus stop, no readout, just an image with a summary. | Type: boolean | Default true |
| animate Entrance motion. Always off under reduced motion. | Type: boolean | Default true |
| level Gradient only: coverage of the range bands, from 0 to 1. | Type: number | Default 0.25 |
| matrix Gradient only: the dither texture of the bands. | Type: "bayer2" | "bayer4" | "bayer8" | "blue-noise" | Default "bayer4" |
| cell Size of one dither cell in px, rounded to whole device pixels. | Type: number | Default --dither-cell |
| axes Show the value and category axes. | Type: boolean | Default true |
| tooltip Show the readout while scrubbing. | Type: boolean | Default true |
| legend Show the legend under the chart. Its entries spotlight a series on hover and lock it on click. | Type: boolean | Default Two or more series |
| format How values are written on the axis, in the readout and in the table. | Type: Intl.NumberFormatOptions | ((value: number) => string) | Default Compact, as in 1.2K |
Series colours follow the palette: ink, then orange, green, pink, purple, blue, red and grey. Pass colors, or color on one series, with a palette name or any CSS colour.
Notes
- The chart is a focusable group with a short summary, and every value, including ranges, is in a visually hidden table. The arrow keys, Home and End move the readout without easing, a polite live region reads out each stop, and Escape clears the readout and any locked series. With
bloom, each line gets a soft glow in its colour. - On entrance solid lines draw from left to right, dashed lines fade in, and bands step up through the dither levels. With reduced motion the finished chart appears at once.
- Markers carry a 2px ring in
--dither-paperso they stay legible where lines cross. Set--dither-paperto your card's background if it differs from the page.