Horizon
Horizon folds a wide-range series into bands so magnitude reads as layer opacity in a row a few pixels tall.
- encodes
- position + fold-layer opacity (darker = higher band)
- precision
- low — a density read; Sparkline when exact shape matters
- nodes
- ≤ 6 (≤ 3 fold paths per direction)
Horizon prints a whole wide-range series in a 14-pixel row. It cuts the series into bands and folds them, so layer opacity carries magnitude and extremes stay visible at heights where a sparkline would flatten into noise. Two folds is the default, because two read approximately without training; three trade learnability for density, so reserve them for ranges that genuinely span them.
How to read it — every band rises from the row's bottom edge, positive or negative; darker means farther from the baseline, not which direction. Above-baseline values shade in the accent color, below-baseline in the negative color, so direction is never color-alone even though both fold upward the same way.
Install
import { Horizon } from "@microcharts/react/horizon";// cpuLoad — real values under “Sample data” below<Horizon data={cpuLoad} title="Cluster load" />Sample data
const cpuLoad = [ 2, 5, 9, 14, 22, 31, 26, 18, 12, 24, 38, 45, 41, 30, 19, 11, 6, 3, 8, 16, 27, 35, 29, 20,];Try it
When to use it
Use it for dense monitoring rows (dozens stacked) and wide-range series in tight cells. Folding needs a key, so skip it for first-glance audiences; with a few rows and room to spare, use Sparkline.
Sizing
Variants
baseline is authored, never inferred: a fold origin is a claim about what "normal" means.
format also takes Intl.NumberFormatOptions — with a locale, the accessible summary's range and last-value numbers
follow that locale's own grouping ("4.500" in German, not "4,500"). The interactive readout's per-point values localize
the same way; the folded geometry never changes.
Edge cases
Negative values take the negative token and fold from the same bottom edge as positive values (the default
mode="mirror"). Both directions get darker as they move away from the baseline, so density means distance from the
baseline rather than sign. Identical values across the whole row still render as a solid block rather than vanishing,
keeping "flat" visibly distinct from "no data."
Four homes
Accessibility
The accessible name reads the unfolded series — "Trending up 900%. Range 2 to 45. Last value 20." — so screen-reader users get the true values, not the folded geometry. The interactive readout announces unfolded values per point.
The interactive entry follows the shared interaction contract:
arrow keys rove between units on both axes, Home and End jump to the ends, and a click, tap, Enter or
Space selects a unit — pinning its readout so it survives blur, until you select it again or press Escape.
On touch, a tap pins and a drag scrubs.
Props
| Prop | Type | Notes |
|---|---|---|
| data* | (number | null)[] | Series over time. |
| folds | 2 | 3 | Band count — 3 only when the range genuinely spans it. |
| mode | "mirror" | "offset" | Mirror flips negatives upward (denser); offset keeps up/down. |
| baseline | number | Fold origin (e.g. a target level) — authored, never inferred. |
| animate | boolean | interactiveOpt-in entrance motion when the chart mounts client-side — add import "@microcharts/react/motion" once. Inert on the server, on hydrated server HTML, and under prefers-reduced-motion. |
Plus the shared grammar — data, domain, color, title, summary, format — and the layout props (width, height, className, style) that every chart accepts. Interactive entries also share animate and live, and — wherever a chart has more than one navigable unit — onActive, onSelect, selectedIndex and defaultSelectedIndex; and — wherever the chart shows a hover value — readout. See the shared grammar.