For the complete documentation index, see llms.txt. Markdown variants are available by appending .md to any URL or sending an Accept: text/markdown header. The dithercn agent skill, for building any dither on the engine, is at /.well-known/agent-skills/dithercn/SKILL.md.
dithercn

Bar chart

Dithered bars with a solid cap, grouped or stacked, for weekly counts and period comparisons.

Controls
Fill
Colour
Bloom
Solid bar
Cell3px
Interactive

Installation

pnpm dlx shadcn@latest add https://dithercn.dev/r/bar-chart.json

Usage

import { BarChart } from "@/components/dithercn/bar-chart";
 
const data = [
  { week: "W36", deploys: 61 },
  { week: "W37", deploys: 68 },
  { week: "W38", deploys: 74 },
];
 
export function Deploys() {
  return (
    <BarChart
      data={data}
      x="week"
      series={[{ label: "Deploys", value: "deploys" }]}
    />
  );
}

Each bar is a dithered body under a solid 2px cap. One bar is drawn solid, the last by default, with its value above it. When the pointer or keyboard focus moves to another bar, that one fills in instead. x and each series' value name fields of your data, or take a function when you render from a Client Component.

Examples

Stacked

layout="stacked" puts the series on top of each other with a 2px gap between segments, and the axis covers the totals. highlight="max" picks the tallest stack.

Grouped comparison

Grouped bars sit side by side, one per series. highlight="none" leaves every bar dithered until you scrub to one, and fill="hatched" gives each bar diagonal lines instead of the gradient.

Props

PropTypeDefault
data*

One entry per bar or group of bars, in order.

Type: readonly T[]Default –
x*

The field (or function) holding each bar's category label.

Type: keyof T | ((datum: T) => string | number)Default –
series*

Each series is { label, value, color? }, where value is a numeric field of T or a function.

Type: [ChartSeries<T>, ...ChartSeries<T>[]]Default –
layout

How several series share a category: side by side, on top of each other, or stacked and scaled to 100%.

Type: "grouped" | "stacked" | "percent"Default "grouped"
fill

gradient is densest at the top of each bar and thins toward the axis. dotted is a sparse lattice, hatched diagonal lines, solid flat colour.

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: booleanDefault true
animate

Entrance motion. Always off under reduced motion.

Type: booleanDefault true
highlight

The bar drawn solid, with its value, while nothing is scrubbed.

Type: number | "last" | "max" | "none"Default "last"
height

Height in px, axes included.

Type: numberDefault 240
level

Gradient only: coverage at the top of each bar, from 0 to 1.

Type: numberDefault 0.75
matrix

Gradient only: the dither texture.

Type: "bayer2" | "bayer4" | "bayer8" | "blue-noise"Default "bayer4"
cell

Size of one dither cell in px, rounded to whole device pixels.

Type: numberDefault --dither-cell
axes

Show the value and category axes.

Type: booleanDefault true
tooltip

Show the readout while scrubbing.

Type: booleanDefault true
legend

Show the legend under the chart. Its entries spotlight a series on hover and lock it on click.

Type: booleanDefault 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 is in a visually hidden table. The arrow keys, Home and End move between bars, a polite live region reads out each stop, and Escape clears the readout and any locked series. The scrubbed column gets a soft band and fills in solid.
  • Bars rise from the baseline one after another on entrance. With reduced motion they appear in place.
  • Bars are capped at 36px wide and snapped to whole pixels, and the dither cell to whole device pixels, so edges and cells stay sharp.