Sparkline
A tiny, word-sized trend line, area, bar, or occupancy chart, single-tone and axis-free, for inline use or a live time-series.
Live demo
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.
Props
13 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
Appearance
Variants
line
A stroked trend line, the default.
area
The line with a soft fill down to the baseline.
bar
A run of tiny bars, one per value.
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
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
The inline-block root sized by --spark-width / --spark-height, tinted by --spark-color.
line
The trend polyline (or the area's outline), stroked in the tone.
end
The dot marking the most recent value.
occupancy-track
The faint full-width rail behind an occupancy strip that shows the empty (off) slots.
occupancy-block
A filled block for each "on" sample of an occupancy strip, in the tone.
marker
The hover guide, dot, and value tooltip shown while sweeping across the chart.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
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
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" />