Sparkline
A tiny line with no axes, to show a trend beside a number.
SourcePackage @kerf-js/veneer
Preview
tsx
import { Card, Grid, Sparkline, Stack, Text} from '@kerf-js/veneer'export function Stats() { return ( <Grid min="sm" cols={3} gap="related" > {PULSES.map(({ name, value, values, format }) => ( <Card key={name}> <Stack gap="related" role="img" aria-label={ `${name}: ${value}, ` + 'rising over 12 months' } > <Text role="label" subtle > {name} </Text> <Text role="heading">{value}</Text> <Sparkline values={values} format={format} /> </Stack> </Card> ))} </Grid> )}const TRAFFIC = [ { month: 'Nov', requests: 41.2, errors: 0.42, deploys: 88, cost: 6120, }, { month: 'Dec', requests: 52.8, errors: 0.51, deploys: 74, cost: 6480, }, { month: 'Jan', requests: 47.1, errors: 0.38, deploys: 96, cost: 6390, }, { month: 'Feb', requests: 49.6, errors: 0.33, deploys: 104, cost: 6710, }, { month: 'Mar', requests: 58.3, errors: 0.47, deploys: 118, cost: 7240, }, { month: 'Apr', requests: 61.9, errors: 0.29, deploys: 126, cost: 7380, }, { month: 'May', requests: 66.4, errors: 0.31, deploys: 131, cost: 7690, }, { month: 'Jun', requests: 64.2, errors: 0.44, deploys: 122, cost: 7620, }, { month: 'Jul', requests: 71.5, errors: 0.27, deploys: 139, cost: 8010, }, { month: 'Aug', requests: 74.8, errors: 0.25, deploys: 144, cost: 8230, }, { month: 'Sep', requests: 79.3, errors: 0.36, deploys: 152, cost: 8560, }, { month: 'Oct', requests: 86.1, errors: 0.22, deploys: 167, cost: 8930, },]const min = 60_000const dollars = (v: number) => `$${v.toLocaleString('en-US')}`const millions = (v: number) => `${v}M`const PULSES = [ { name: 'Requests', value: '86.1M', values: TRAFFIC.map((t) => t.requests), format: millions, }, { name: 'Deploys', value: '167', values: TRAFFIC.map((t) => t.deploys), }, { name: 'Bill', value: '$8,930', values: TRAFFIC.map((t) => t.cost), format: dollars, },]Installation
npm install @kerf-js/veneerUsage
tsx
import { Sparkline } from '@kerf-js/veneer'Colours
color takes any Veneer token.
tsx
import { Row, Sparkline, Stack, Text} from '@kerf-js/veneer'export function Colors() { return ( <Stack gap="group" role="img" aria-label="Errors falling, deploys rising" > <Row gap="related" justify="between" > <Text role="label">Error rate</Text> <Text subtle>0.22%</Text> </Row> <Sparkline values={ERRORS} color="var(--veneer-color-critical-solid)" format={percent} /> <Row gap="related" justify="between" > <Text role="label">Deploys</Text> <Text subtle>167</Text> </Row> <Sparkline values={DEPLOYS} color="var(--veneer-color-positive-solid)" /> </Stack> )}const TRAFFIC = [ { month: 'Nov', requests: 41.2, errors: 0.42, deploys: 88, cost: 6120, }, { month: 'Dec', requests: 52.8, errors: 0.51, deploys: 74, cost: 6480, }, { month: 'Jan', requests: 47.1, errors: 0.38, deploys: 96, cost: 6390, }, { month: 'Feb', requests: 49.6, errors: 0.33, deploys: 104, cost: 6710, }, { month: 'Mar', requests: 58.3, errors: 0.47, deploys: 118, cost: 7240, }, { month: 'Apr', requests: 61.9, errors: 0.29, deploys: 126, cost: 7380, }, { month: 'May', requests: 66.4, errors: 0.31, deploys: 131, cost: 7690, }, { month: 'Jun', requests: 64.2, errors: 0.44, deploys: 122, cost: 7620, }, { month: 'Jul', requests: 71.5, errors: 0.27, deploys: 139, cost: 8010, }, { month: 'Aug', requests: 74.8, errors: 0.25, deploys: 144, cost: 8230, }, { month: 'Sep', requests: 79.3, errors: 0.36, deploys: 152, cost: 8560, }, { month: 'Oct', requests: 86.1, errors: 0.22, deploys: 167, cost: 8930, },]const ERRORS = TRAFFIC.map((t) => t.errors)const percent = (v: number) => `${v}%`const DEPLOYS = TRAFFIC.map((t) => t.deploys)One per table row
It fills the width it is given, so a flexible cell lines the rows up. The ones getting slower turn amber.
tsx
import { Row, Sparkline, Stack, Text} from '@kerf-js/veneer'export function Rows() { return ( <Stack gap="related" divided > {SERVICES_WEEK.map(({ name, now, trend, values }) => ( <Row key={name} gap="group" wrap={false} role="img" aria-label={ `${name}: p95 ${now} ms, ` + `${trend} over two weeks` } > <Text role="label" style="inline-size: 7rem" > {name} </Text> <div style="flex: 1; min-inline-size: 0"> <Sparkline values={values} format={ms} color={ trend.startsWith('rising') ? 'var(--veneer-color-caution-solid)' : undefined } /> </div> <Text subtle style="inline-size: 4.5rem; text-align: end" > {now} ms </Text> </Row> ))} </Stack> )}const min = 60_000const ms = (v: number) => `${Math.round(v)} ms`const WEEK = (base: number, drift: number) => Array.from({ length: 14 }, (_, i) => Math.round( base + drift * i + Math.sin(i * 1.7 + base) * base * 0.08, ), )const SERVICES_WEEK = [ { name: 'storefront', now: 84, values: WEEK(90, -0.6), trend: 'falling', }, { name: 'checkout', now: 112, values: WEEK(108, 0.4), trend: 'steady', }, { name: 'search-api', now: 146, values: WEEK(118, 2.2), trend: 'rising', }, { name: 'mobile-bff', now: 208, values: WEEK(160, 3.6), trend: 'rising fast', },]When to use
Use it for
- A small trend next to a figure in a card or table row
Reach for something else
- LineChart, when people need to read the values
Accessibility
- A sparkline is hidden from screen readers. Give its parent a label that says what the trend means.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
values | number[] | — | The numbers to draw, oldest first. |
color | string | — | The line's colour: a Veneer token such as var(--veneer-color-positive-solid), or any CSS colour. |
format | ((value: number) => string) | — | Turns a value into the text shown. |
Parts
Every element carries a stable name, so you can style it from your own CSS: [data-scope="sparkline"] [data-part="spark"]
spark
RelatedLine chartStream chart