A lightweight accessible SVG trend line with a marked latest value and no chart dependency.
Weekly signups
1,208
Up this week
import { Sparkline } from "@/components/ballmac/sparkline"
export default function SparklineDemo() {
return (
<div className="flex w-full max-w-sm items-end justify-between gap-4 rounded-xl border border-border bg-card p-5">
<div>
<p className="text-muted-foreground text-sm">Weekly signups</p>
<p className="mt-2 text-2xl font-semibold tabular-nums">1,208</p>
<p className="text-primary mt-1 text-xs font-medium">Up this week</p>
</div>
<Sparkline
className="max-w-36"
values={[8, 12, 11, 17, 14, 20, 23, 21, 26]}
label="Weekly signups"
/>
</div>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/sparklineAdd the Ballmac items it builds on.
$ pnpm dlx shadcn@latest add @ballmac/i18nCopy the source into your project.
components/ballmac/sparkline.tsx// Ballmac UI: Sparkline. https://ui.ballmac.com/components/sparkline "use client" import * as React from "react" import { cn } from "@/lib/utils" import { useMessages } from "@/lib/ballmac/i18n" type SparklineProps = Omit<React.ComponentProps<"svg">, "values"> & { /** Ordered numeric samples. */ values: number[] /** Accessible description of the series. */ label: string /** Height in pixels. */ height?: number /** Show the last data point. */ showEnd?: boolean } function Sparkline({ className, values, label, height = 48, showEnd = true, ...props }: SparklineProps) { const msg = useMessages() const safe = values.filter(Number.isFinite) const minimum = Math.min(...safe) const maximum = Math.max(...safe) const span = maximum - minimum || 1 const points = safe.map((value, index) => ({ x: safe.length === 1 ? 50 : 4 + (index / (safe.length - 1)) * 92, y: 42 - ((value - minimum) / span) * 34, })) const path = points .map( (point, index) => `${index ? "L" : "M"}${point.x.toFixed(2)} ${point.y.toFixed(2)}`, ) .join(" ") const last = points.at(-1) return ( <svg data-slot="sparkline" role="img" aria-label={safe.length ? msg("sparkline.range", "{label}: from {from} to {to}", { label, from: safe[0], to: safe.at(-1) ?? "" }) : msg("sparkline.noData", "{label}: no data", { label })} viewBox="0 0 100 48" preserveAspectRatio="none" height={height} className={cn("text-primary w-full overflow-visible", className)} {...props} > {safe.length > 1 && ( <path d={path} fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" vectorEffect="non-scaling-stroke" /> )} {showEnd && last && ( <circle cx={last.x} cy={last.y} r="3" fill="currentColor" vectorEffect="non-scaling-stroke" /> )} </svg> ) } export { Sparkline, type SparklineProps }Update the import paths to match your project setup.
Usage
import { Sparkline } from "@/components/ballmac/sparkline"The full example is in the Code tab above.
Examples
States and variants
import { Sparkline } from "@/components/ballmac/sparkline"
export default function SparklineStates() {
return (
<div className="grid w-full max-w-xs gap-4">
<Sparkline values={[9, 8, 7, 6, 5, 4]} label="Declining trend" />
<Sparkline values={[]} label="No samples" />
</div>
)
}API reference
| Prop | Type | Default |
|---|---|---|
values*Ordered numeric samples. | number[] | — |
label*Accessible description of the series. | string | — |
heightHeight in pixels. | number | 48 |
showEndShow the last data point. | boolean | true |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| None | Trend description is announced as an image |
Use with AI
A lightweight accessible SVG trend line with a marked latest value and no chart dependency. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Sparkline (@ballmac/sparkline) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Show a small trend beside a metric
Not for
- Use a full chart for axes and detailed inspection
Registry JSON: https://ui.ballmac.com/r/sparkline.json
Credits
Free to use in personal and commercial projects.
- npm
- None
- Registry
- @ballmac/i18nshadcn/utils
Pairs well with
Stat Card
A metric card with a clear comparison trend, optional visual, and readable value hierarchy.
Activity Feed
A compact actor and action feed with timestamps and an empty state.
Audio Player
A podcast-style audio player: play and pause, skip back and forward, a scrubber with chapter ticks, a named current chapter and a speed control. Works with a real file or as a silent demo.
Avatar Circles
Overlapping avatars that spread on hover, lift individually with a name and role tooltip, show presence, fall back to initials and end in a +N count that can be a button.