ComponentsData & statusSparkline
On this page
Data & status

Sparkline released

One series drawn small where its values already stand: a line, bars or a band.

  • Import@pacit/components/sparkline
  • Selectorpct-sparkline

No APG pattern applies, and none is invented — the reasoning is under Accessibility ↓

RoomLast 8 hkWh
Hall1.7
Attic0.8
Kitchen0.9

sparkline.demo.ts — the same file runs here and is highlighted under Code

Start here

Usage

One import per component, and the shortest possible use — the package itself is installed once, on the start page.

Import this component
import { PctSparkline } from '@pacit/components/sparkline';
ExportedWhat it is
PctSparkValuetypeunreleasedkeyof T | ((row: T, index: number) => number | null | undefined)What a sparkline reads off a row: a key of the row holding a number, or a function of the row and its index; unset, the rows are the numbers.
PctSparklinecomponentunreleasedpct-sparklineA sparkline: one series drawn small, for a table's cell or a card — a line, bars or a band of on and off, with no axes, no cursor and no legend (0117).
<th scope="row" id="hall">Hall</th>
<td>
  <pct-sparkline [data]="hours()" unit="kWh" [area]="true" ariaLabelledby="hall" />
</td>
Examples

Every state, running

Each example is the component itself, not a screenshot. Open the code under any of them.

A balance as bars, a heater as a band

Bars rise from nought, so a day the house gave back to the grid hangs below the line; the lowest and the highest day stand as dots. A band draws a state that is on or off — a value of a half or more is on — and says the last one in words.

Balance, last 7 daysHeater, last 10 hours
API

What it reads, and what it writes back

Read from the source at build time — the types, the defaults and the lines of documentation are the library's own.

unreleased marks 15 of them: on main, and not in @pacit/components@0.3.0 — the next release names the version.

A sparkline: one series drawn small, for a table's cell or a card — a line, bars or a band of on and off, with no axes, no cursor and no legend (0117).

Inputs

NameTypeWhat it does
dataunreleaseddefault []readonly T[]The rows, in order: each a value, or a row value reads one from.
valueunreleaseddefault nullPctSparkValue<T> | nullHow to read a row's value: a key or a function; unset, each row is its value. Anything that is not a finite number is a gap.
kindunreleaseddefault 'line''line' | 'bars' | 'band'The drawing: a line, a bar per value, or a band with each slot on (a value of a half or more) or off.
areaunreleaseddefault falsebooleanA line filled to the floor.
lastunreleaseddefault truebooleanThe last value as a dot, on a line or bars.
extremesunreleaseddefault falsebooleanThe lowest and the highest value as dots, on a line or bars.
floorunreleaseddefault nullnumber | nullThe value at the bottom of the box; unset, the lowest value — or nought for bars, which rise from it.
ceilingunreleaseddefault nullnumber | nullThe value at the top of the box; unset, the highest value — or nought for bars.
colorunreleaseddefault ''stringThe colour of the drawing, any CSS colour; unset, sparkline-fg.
unitunreleaseddefault ''stringThe unit the description writes after each value; '' is a plain number.
localeunreleaseddefault ''stringThe locale of the description's numbers: the application's when unset.
ariaLabelunreleaseddefault ''stringThe picture's name — what is drawn. Give this or ariaLabelledby.
ariaLabelledbyunreleaseddefault ''stringAs ariaLabel, for a name that already stands on the page — a row's header, say.

Outputs

None of its own. The native events — (click), (focus), the form's submit — are the platform's, and two-way models above carry the rest.

What it writes on the element

The hooks a stylesheet or a test can hold. Stable by contract — the parts inventory gate fails the build when one disappears.

AttributeBound toNote
classpct-sparkline
roleimg
aria-labelariaLabel() || null
aria-labelledbyariaLabelledby() || null
aria-describedbyids.description
data-pct-kindkind()
[style.--_pct-sparkline-color]propertycolor() || null
Styling

Parts to select, tokens to override

Two ways in, both stable. No class names to depend on.

Parts

In a test, PctSparklineHarness from @pacit/components/testing reads a part by the name below and a state by its attribute — the harness gate holds the names to the package.

PartSelectorWhat it is
area[data-pct-part="area"]the fill under the line, down to the floor
bar[data-pct-part="bar"]a value as a bar, from nought held in the range
dot[data-pct-part="dot"]the last value, or the lowest or the highest
line[data-pct-part="line"]the series as a line, broken at each gap
picture[data-pct-part="picture"]the drawing, hidden: the host is the image a reader meets
slot[data-pct-part="slot"]a value of a band, filled when on

Tokens

Every value the component reads, with the light and the dark default. Override a token at any scope and it cascades like any custom property.

TokenStyles
--pct-sparkline-area-alpha20%How much of the drawing's colour fills the area under the line.
--pct-sparkline-band-bg{pct.surface-100}#f1f5f9 · #1e293bA band's off-slot: the surface's own shade.
--pct-sparkline-dot-fg{pct.primary}#2563eb · #60a5faThe dots, unless the consumer gives a colour.
--pct-sparkline-dot-size0.4emThe width of the dots on the last value and on the extremes.
--pct-sparkline-fg{pct.primary}#2563eb · #60a5faThe drawing: the line, the bars and a band's on-slots, unless the consumer gives a colour.
--pct-sparkline-width1.5pxThe width of the line, kept whatever the box is stretched to.

Theming

A brand override in a few declarations. Scope it to a theme attribute, a page or one container — the component never notices.

[data-theme='brand'] {
  --pct-sparkline-fg: #0f766e;
  --pct-sparkline-width: 2px;
}
RoomLast 8 hkWh
Hall1.7
Attic0.8
Kitchen0.9
Accessibility

Measured, not assumed

Pattern: none — a sparkline is a picture, not a widget. The host carries the img role, named by the consumer and described by the words the picture holds. Named in the class JSDoc.

Keyboard

keyeffecttest

Empty by design: a picture is no stop. A reader meets it in reading order, named by its row and described by its values; apps/sandbox-e2e/src/sparkline.spec.ts asks every element in it for a tab index and finds none.

What is measured, and what is not yet

Every line is a test in three engines or a gate on every commit. The gaps are listed the same way, each with the requirement that will close it.

MeasuredARIA pattern named in the class JSDoclibs/components/sparkline/src/sparkline.ts — the img role on the host
MeasuredKeyboard map tested key by keyapps/sandbox-e2e/src/sparkline.spec.ts — the empty map is the claim: no element in the component has a tab index
Measuredaxe audit on the component's own sandbox viewapps/sandbox-e2e/src/a11y.spec.ts — /sparkline in SBX_ROUTES
GapVisual screenshotnone — gap: no baseline yet; the geometry a screenshot would show is measured instead in apps/sandbox-e2e/src/sparkline.spec.ts — the box of six by one em, the line at its width, the dot round and at the right edge
Measuredforced-colors: active — no state carried by colour alonelibs/components/sparkline/src/sparkline.scss — the line, the bars, a band's on-slots and the dots in CanvasText, the area left empty, an off-slot an edge on Canvas
n/aprefers-reduced-motion — duration from a tokennot applicable — no motion at all
n/aTouch target ≥ 24×24 px outrightnot applicable — nothing here is a target
MeasuredSize axis aligned to --pct-control-height-*deliberately not: a sparkline is no control, and its box is a word's, sized by the consumer's CSS
GapDensity axisnone — gap: the same one every component here has (req-token-density)
MeasuredRTL — no physical properties + a dir="rtl" screenshotlogical properties only, held by check-styles; a series runs left to right in every direction (direction: ltr on the drawing), as the chart's axis does; no screenshot, as above
MeasuredSSR + hydration with no NG05xxapps/sandbox-e2e/src/hydration.spec.ts — /sparkline in SBX_ROUTES; the server draws the whole picture, nothing is measured
n/aFormsnot applicable — a picture holds no value a form owns
MeasuredParts registered in the inventorylibs/components/parts.snapshot.md, tools/check-parts.mjs — the Parts row above
MeasuredTokens registered + an entry in contrast.policy.jsonlibs/tokens/src/contrast.policy.json — the drawing and the dots on both surfaces (UI error, 4), a band's off-slot on the surface (UI warn); libs/tokens/tokens.snapshot.md
MeasuredStrings through PCT_TEXTSlibs/components/core/src/texts.ts — sparklineLast, sparklineEmpty; libs/components/texts.snapshot.md
MeasuredEntrypoint size budgetlibs/components/size.snapshot.md — the ./sparkline row, on ./core alone
MeasuredScreen-reader logRead 2026-11-05 from docs/acr/at/ — three readers, whole sandbox. Not reachable by this instrument, which is the claim itself: a sparkline takes no focus, and /sparkline walks from the page to the demo's one button (Next hour button main, Next hour · button.) and out to the scaffold, past every picture. What a reader hears on meeting one — its row's name and the sentence of aria-describedby — needs a reader asked to READ the table rather than walk it
MeasuredA docs page with live examplesapps/sandbox/src/app/views/sparkline/ (the sandbox view). The published site: /components/sparkline — prerendered, the demo's own source is the code tab
MeasuredUnit + mutationlibs/components/sparkline/src/sparkline.spec.ts — 11 cases; libs/components/mutation.snapshot.md — sparkline.ts
Evidence

What the repository holds for this component

Read at build time from the tracked snapshots, not typed here.

97.73%mutation score215 of 220 mutants killed
4 × 3e2e cases × enginesplus the shared audits: axe, forced colors, preferences, size, hydration
5colour pairs measuredlight and dark, at every token build
0visual baselinesChromium, the sandbox's own pictures
37elements in the preview0 listeners · 1 render to settle · 7372 µs in jsdom, held exactly but for the clock

Why it is built this way

A chart in a word's space (0117): no axes, no cursor, no legend and no table, because the values stand in the row beside it. It is an entrypoint of its own and imports nothing of @pacit/components/chart, so a table of a hundred rows pays for a few kilobytes and not for a scene.

Known limitations

  • One series. Two lines in a cell are a chart, and pct-chart draws them with the axes that make two series readable.
  • No tooltip and no cursor. A value under the pointer is what the row beside it already says; a sparkline that took the pointer would be a target and a stop.
  • No decimation. A sparkline of thousands of values draws a path of thousands of points; a cell's worth of values is the case it is built for.
  • No screenshot baseline. The picture's geometry is measured in the e2e spec rather than compared with an image.