Skip to content

Sparkline

Terminal window
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/sparkline.json
See it liveBeside a total and beside a row, against a stated ceiling and against its own range.→

No axes, no gridlines, no ticks — the shape of a history rather than a chart of it. 61 → 74 → 82 reads perfectly at three points and stops working at ten; a line holds both, and the exact figures stay in the table underneath.

The box is drawn at the size it is shown at, which is why size is a variant and not a className. A viewBox wider than the element scales x and y by different factors: the line bends away from the data and the end dot stretches into a wedge. The donor took a size object and a class, so every call site repeated itself — size={{ width: 52, height: 16 }} className="h-4 w-[52px]" — and the two could drift apart.

<Sparkline values={trend} max={100} label="Score, 61 to 82" />
<Sparkline values={axisTrend} max={axis.scale} size="sm" label={`${axis.label}, 3 to 4`} />

max is the ceiling of the scale, not the top of the data. Without it the line is scaled against its own range, and a work that moved 61 → 63 climbs the whole box — a small change drawn as a transformation. Give the axis maximum and two lines can be compared by eye. A value that overshoots the stated ceiling still stays inside the frame: it is the caller’s problem to notice, not a reason to draw a line leaving the box.

The line does not judge. It is drawn in the de-emphasis tone with the newest point in the accent, so the eye finds where it is now without the line claiming what that means. The donor coloured it green when it ended higher and red when it ended lower, which the component cannot support: for time-to-answer or error rate, down is the good direction. Where the caller genuinely knows, tone says so — and the meaning usually belongs next to the line anyway, in a StatTile’s delta.

label is required. The line is an img to a screen reader, and an unlabelled image carrying information is information withheld. Say what the line says: "Score, 61 to 82 over three versions".

Fewer than two points draws nothing. One point has no direction, and a line through it would be a statement about a history there is none of.

It is not interactive, and that is the form rather than an omission: a sparkline has no room for a hover target that is not the whole of it. A reader who needs the figures gets them from the table beside it.

Props

Read from the types in ui/sparkline.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.

PropTypeDefault
tonemuted | accent | good | bad'muted'
valuesrequirednumber[]Oldest first. Fewer than two points is not a line, and draws nothing.
maxnumberThe top of the scale, so two lines can be compared by eye. Defaults to the highest value present, which makes the line self-scaled - fine for one line alone, wrong for a column of them.
labelrequiredstringWhat the line says, for a reader who cannot see it. Required: an unlabelled img is an unlabelled image, and this one carries information.
sizesm | mdsm sits beside a figure in a row, md beside a total. Defaults to md.

And every prop of React's SVGAttributes<SVGSVGElement> except values, passed on.

Accessibility

The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 15 rules applied and passed, and none failed. The accessibility report has every item.

History

Each release that changed ui/sparkline.tsx - the file a product copies - newest first.

  1. v0.41.0
    • docs read every props table from the component's types ee3b159
  2. v0.33.0breaking
    • feat no-raw-button and no-arbitrary-scale ac7a11a
  3. v0.24.0
    • feat the shape of a history, beside the number it belongs to 132bcbe