Skip to content
kerfjsGitHub

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/veneer

Usage

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

PropTypeDefaultDescription
valuesnumber[]—The numbers to draw, oldest first.
colorstring—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