Skip to main content

Sparkline

Info:Metrics
37

A tiny, word-sized trend line, area, bar, or occupancy chart, single-tone and axis-free, for inline use or a live time-series.

mini chart trend line inline chart time series micro chart

Live demo

live · @xtyle/astro

Sparkline

Line, area, and bar

The same series as three shapes. Sweep across one for the value at the nearest point.

Beside a stat

Signups

1,284

Latency

212ms

Errors

0.4%

Time-windowed series

points with real timestamps sit at their true position across the window instead of assuming even spacing, so irregular samples read honestly. step holds each value for an on/off signal.

signal (irregular samples)

uptime (stepped on/off)

Occupancy strip

variant="occupancy" fills each "on" sample of a bool series as a solid block over a faint track, the uptime/presence reading a 0/1 step line can't give. Same uptime series, two shapes:

step line

occupancy strip

Kind-aware bounds

bounds ranges a typed metric without hand-math. percent pins [0, 100] so a reading sits at its true fraction of full height; duration caps at a rolling power of two, so one spike lifts the ceiling instead of squashing everything below it.

CPU % (bounds="percent")

Latency ms (bounds="duration")

Units in the hover readout

format makes the hover value speak the metric's units — sweep across these and the readout reads 1.5m or 87%, not a bare number.

Latency (format="duration")

Load (format="percent")

Inline in text

Signups climbed 34% this week, while error rate fell to 0.4%.

Empty state

An empty series shows a muted No data label instead of a blank box, so a metric with no samples yet reads as intentional.

No data

Sparkline draws a single series of numbers as a small, axis-free SVG that reads as a trend at a glance, sized to sit inline in a sentence, a table cell, or beside a Stat. Four shapes: a line, a filled area, a mini bar run, or an occupancy strip that fills each "on" sample of a bool/binary series as a solid block (uptime, presence, connection state) rather than drawing a 0/1 line.

Feed it evenly-spaced values, or switch to points (timestamped samples) for a real time-series: they map onto a sliding window (or an explicit domain), so irregular samples sit at their true position and slide left over real time. Add step to hold each value for an on/off signal. It takes one tone from the theme roster (any semantic role or named hue) and marks the latest point with an end dot. It's interactive: sweeping across it floats a marker and the value at the nearest point, and format makes that readout speak the metric's units (duration as 42s/1.5m, percent as 87%, bytes, or a plain unit). Size it with the --spark-width and --spark-height custom properties; give it a label for an accessible name. Auto-ranged from the data, pinned with min / max, or ranged by kind with bounds: percent locks [0, 100], unit locks [0, 1], and duration caps at a rolling power of two so a latency spike lifts the ceiling instead of squashing the baseline; the per-kind range every consumer of a typed metric would otherwise re-derive by hand. An empty series shows a muted No data label instead of a blank box.

When to use

How this component composes with the rest of the set.

Drop one beside a Stat value for a headline number with its recent trend, both in the Metrics family.
Set --spark-width / --spark-height small to sit a sparkline inline in running text or a table cell.
Pin the same min / max across a column of sparklines so their heights compare honestly.

Props

13 props, straight from the manifest.

PropTypeDefaultBindingsDescription
values number[]
html svelte astro
The data series, evenly spaced. JS property in html/svelte, a JSON or comma list attribute, or a prop in astro.
points { at: number | string; value: number }[]
html svelte astro
Time-windowed mode: timestamped samples (`at` is epoch ms or a date string) placed on a real time axis instead of even spacing, so irregular samples read at their true position and slide left over time. JS property in html/svelte, JSON attribute or prop in astro. Pair with `window` or `domain`; overrides `values` when set.
window number 300000
html svelte astro
The sliding time window in ms (default 5 minutes), ending at now; samples older than it drop off the left. Used only in `points` mode, and ignored when `domain` is set.
domain [number | string, number | string]
html svelte astro
An explicit `[start, end]` time span (epoch ms or date strings) for the x-axis, instead of the sliding `window`. JSON attribute or prop.
step boolean false
html svelte astro
Draws the line as a sample-and-hold step, so an on/off or discrete series reads as held levels rather than diagonal ramps.
variant "line" | "area" | "bar" | "occupancy"
line area bar occupancy
line
html svelte astro
The shape: a stroked line, a filled area under it, a mini bar run, or an `occupancy` strip that fills each "on" sample of a bool series as a solid block. Pair `occupancy` with `bounds="unit"` for a clean 0/1 threshold.
tone SparklineTone
accent neutral danger success warn info accent-2 accent-3 accent-4 red orange yellow green blue purple brown pink cyan gray white black
accent
html svelte astro
The single color, any semantic role (accent, success, danger, …) or named hue (red … black).
showEnd boolean true
html svelte astro
Marks the latest point with a dot (line and area only). Kebab `show-end` on the element.
min number
html svelte astro
Pin the low end of the range; omit to auto-range from the data. Share across a column for a common baseline.
max number
html svelte astro
Pin the high end of the range; omit to auto-range from the data.
bounds "percent" | "unit" | "duration"
percent unit duration
html svelte astro
Kind-aware auto y-bounds for a typed metric, so you don't re-derive the same range at every call site. `percent` → `[0, 100]`; `unit` → `[0, 1]` (a fraction or bool); `duration` → `[0, rolling power-of-two cap]` so a spike lifts the ceiling instead of squashing the baseline. An explicit `min`/`max` overrides it.
format "percent" | "duration" | "bytes" | "unit"
percent duration bytes unit
html svelte astro
Speak the metric's units in the hover readout instead of a bare number: `duration` reads seconds as `42s` / `1.5m` / `0.3h`, `percent` appends `%`, `bytes` steps `B`/`KB`/`MB`/…, `unit` is a trimmed number. Independent of `bounds` (range vs. label); unset shows the raw value.
label string
html svelte astro
An accessible name for the chart.

Appearance

Variants

line

.xtyle-sparkline--line

A stroked trend line, the default.

area

.xtyle-sparkline--area

The line with a soft fill down to the baseline.

bar

.xtyle-sparkline--bar

A run of tiny bars, one per value.

occupancy

.xtyle-sparkline--occupancy

A filled block per "on" sample of a bool/binary series over a faint track; the on/off reading a step line can't give.

States

hover

.xtyle-sparkline__marker

Sweeping across the chart floats a guide, a dot at the nearest point, and its value.

Anatomy

The named parts that make up the component, with their selectors.

sparkline

.xtyle-sparkline

The inline-block root sized by --spark-width / --spark-height, tinted by --spark-color.

--accent

line

.xtyle-sparkline__line

The trend polyline (or the area's outline), stroked in the tone.

end

.xtyle-sparkline__end

The dot marking the most recent value.

--bg-0

occupancy-track

.xtyle-sparkline__track

The faint full-width rail behind an occupancy strip that shows the empty (off) slots.

--line

occupancy-block

.xtyle-sparkline__block

A filled block for each "on" sample of an occupancy strip, in the tone.

marker

.xtyle-sparkline__marker

The hover guide, dot, and value tooltip shown while sweeping across the chart.

--line-2 --surface-overlay --surface-overlay-border --elevation-3 --fg-0

Tokens & coverage

What the component consumes, checked live against what the algorithm produces.

Success:fully covered 37/37 consumed tokens produced default register: 299 tokens

Live coverage check against the xtyle-default register (derive(xtyleDefault, { anchors })coverComponent(manifest, register)). Every token this component consumes must be a key the algorithm produces.

--accent --accent --accent-2 --accent-3 --accent-4 --bg-0 --black --blue --border-thin --brown --cyan --danger --elevation-3 --fg-0 --fg-2 --font-sans --gray --green --info --leading-normal --line --line-2 --neutral --orange --pink --purple --radius-md --red --space-1 --space-2 --success --surface-overlay --surface-overlay-border --text-xs --warn --white --yellow

Accessibility

The SVG carries role="img" and an aria-label (your label, or a value-count fallback), so it announces as a single image rather than a pile of shapes.
Tone is chosen from the theme roster; pair a sparkline with a nearby number or label so meaning never rides on the trend shape or color alone.

Code

Line, area, and bar

The same series as a stroked line, a filled area, and a mini bar run, each in a different tone.

<xtyle-sparkline values="4,6,5,8,7,9,12,10,13" label="Weekly signups"></xtyle-sparkline>

<xtyle-sparkline values="4,6,5,8,7,9,12,10,13" variant="area" tone="success"></xtyle-sparkline>

<xtyle-sparkline values="4,6,5,8,7,9,12,10,13" variant="bar" tone="purple"></xtyle-sparkline>
<script lang="ts">
	import { Sparkline } from "@xtyle/svelte";

	const trend = [4, 6, 5, 8, 7, 9, 12, 10, 13];
</script>

<Sparkline values={trend} label="Weekly signups" />
<Sparkline values={trend} variant="area" tone="success" />
---
import { Sparkline } from "@xtyle/astro";
const trend = [4, 6, 5, 8, 7, 9, 12, 10, 13];
---

<Sparkline values={trend} label="Weekly signups" />
<Sparkline values={trend} variant="area" tone="success" />

Inline in text

Sized down with --spark-width / --spark-height to sit word-height in a sentence.

<p>
	Signups this week
	<xtyle-sparkline values="4,6,5,8,7,9,12,10,13" tone="accent" style="--spark-width: 5rem; --spark-height: 1.2rem;"></xtyle-sparkline>
	up 34%.
</p>
<p>
	Signups this week
	<Sparkline values={trend} style="--spark-width: 5rem; --spark-height: 1.2rem;" />
	up 34%.
</p>
<p>
	Signups this week
	<Sparkline values={trend} style="--spark-width: 5rem; --spark-height: 1.2rem;" />
	up 34%.
</p>

Time-windowed series

points with timestamps on a sliding window place irregular samples at their true position over real time, so a live signal reads honestly instead of assuming even spacing. Add step for a held on/off series.

<xtyle-sparkline window="300000" variant="area" tone="info" label="Signal, last 5m"></xtyle-sparkline>
<script>
	const spark = document.querySelector("xtyle-sparkline");
	// irregular samples over real time: { at: epoch ms | ISO string, value }
	spark.points = history;
</script>
<script lang="ts">
	import { Sparkline } from "@xtyle/svelte";
	// { at: epoch ms | ISO string, value }[]
	const history = readSignalHistory();
</script>

<Sparkline points={history} window={300000} variant="area" tone="info" label="Signal, last 5m" />
---
import { Sparkline } from "@xtyle/astro";
const history = readSignalHistory(); // { at: epoch ms | ISO string, value }[]
---

<Sparkline points={history} window={300000} variant="area" tone="info" label="Signal, last 5m" />

Kind-aware bounds

bounds ranges a typed metric without hand-math: percent pins [0, 100], unit pins [0, 1], and duration caps at a rolling power of two so a spike lifts the ceiling rather than flattening everything else.

<!-- A percent series pins to [0, 100] so 40% reads as 40% of full height -->
<xtyle-sparkline values="38,41,52,49,63,58,71" bounds="percent" tone="info" label="CPU %"></xtyle-sparkline>

<!-- A duration series caps at a rolling power of two, so one spike lifts the ceiling
     instead of squashing the baseline -->
<xtyle-sparkline values="120,90,140,110,760,130,150" bounds="duration" tone="warn" label="Latency ms"></xtyle-sparkline>
<script lang="ts">
	import { Sparkline } from "@xtyle/svelte";
</script>

<Sparkline values={cpu} bounds="percent" tone="info" label="CPU %" />
<Sparkline values={latency} bounds="duration" tone="warn" label="Latency ms" />

Occupancy strip for a bool series

variant="occupancy" fills each "on" sample as a solid block over a faint track; the uptime/presence/connection reading a 0/1 step line can't give. Pair with bounds="unit".

<!-- A bool/occupancy series: "on" samples read as solid blocks, "off" as gaps -->
<xtyle-sparkline values="1,1,0,1,1,1,0,0,1,1" variant="occupancy" bounds="unit" tone="success" label="Uptime"></xtyle-sparkline>
<script lang="ts">
	import { Sparkline } from "@xtyle/svelte";
	// 1 = up, 0 = down
	const uptime = [1, 1, 0, 1, 1, 1, 0, 0, 1, 1];
</script>

<Sparkline values={uptime} variant="occupancy" bounds="unit" tone="success" label="Uptime" />

Units in the hover readout

format makes the hover value speak the metric's units — a latency series reads 1.5m instead of 90, a load series reads 87%. Independent of bounds, which ranges the y-axis.

<!-- Latency in seconds: the hover reads 1.5m, not 90 -->
<xtyle-sparkline values="42,58,90,73,120,66,54" format="duration" bounds="duration" tone="warn" label="Latency"></xtyle-sparkline>

<!-- Load as a percentage: the hover reads 87% -->
<xtyle-sparkline values="38,41,52,49,87,58,71" format="percent" bounds="percent" tone="info" label="Load"></xtyle-sparkline>
<script lang="ts">
	import { Sparkline } from "@xtyle/svelte";
	const latency = [42, 58, 90, 73, 120, 66, 54]; // seconds
	const load = [38, 41, 52, 49, 87, 58, 71]; // percent
</script>

<Sparkline values={latency} format="duration" bounds="duration" tone="warn" label="Latency" />
<Sparkline values={load} format="percent" bounds="percent" tone="info" label="Load" />