Arc
ComponentsBlocksDocsPricingTry the demo
Introduction
Installation
Theming
Motion
AI and MCP
Changelog
Metrics dashboard
KPI drilldown
Metric explorer
Revenue explorer
MRR waterfall
Cohort retention
Usage forecast
Journey flow
Choropleth explorer
Connected scatter
Horizon chartNew
Market terminal
Budget variance
Small multiples
Line replay
AI chat
AI composer
AI answerNew
Model pickerNew
Model arenaNew
Prompt studioNew
Document extractionNew
Agent memoryNew
AI side panel
Agent run
Agent tasksNew
Agent approvalsNew
Agent routinesNew
Agent orchestratorNew
MCP serversNew
Knowledge baseNew
Eval suiteNew
SidebarPro4
Page header
Command palette
Notification center
Selection toolbarNewPro
Empty states
Search resultsPro
Login and sign up5
Onboarding illustrationPro
OTP input
Multi-step formPro
Settings page
Settings command
Security settings
Billing overview
Usage billing
Cancel flow
Team members
Invite people
Team directory
Roles and permissions
API keys
Webhooks
Integrations
Environment variables
Plan comparison
Pricing calculatorPro
Usage pricingPro
Cart drawerPro
Checkout summaryPro
Live checkoutNewPro
Invoice studio
Wallet card
Product listing
Product detail
Inbox triage
Support conversation
Support widget
Help center
File upload
Media playerPro
Week calendar
Availability picker
Project board
Release readiness
Kanban board
Hero section
Hero signupPro
Scroll storyPro
Spotlight gridPro
Feature bentoPro
Comparison table
Logo marquee
Studio perspectivesPro
Stats band
Changelog feed
FAQ section
Contact section
Team showcasePro
Blog grid
Site header
Site footer
CTA section
Newsletter signup
Activity terrain
Revenue globe
Customer galaxy
Semantic zoom
Layout morph
Exploded view
What’s new31 new
BlocksHorizon chart

Horizon chart

Pro

A horizon chart that stacks fourteen daily series as compact folded rows, with a synced crosshair, a sort by name, peak or latest, a band count setting that folds every row on one spring, and a legend that explains how to read the bands.

Live block · sample data

When to use

  • Operations or growth dashboards comparing many markets, services or products against their own baseline over weeks or months.
  • Monitoring views where the reader scans for which rows broke from normal and when, then reads exact values on one day.
  • Any time series comparison where small multiples of line charts would need too much height.

When not to use

  • Use small-multiples when there are only a few series and the reader needs axes and exact levels in each panel.
  • Use ridgeline to compare distributions rather than values over time.
  • Use streamgraph to show how parts add up to a total over time.
  • Use line-chart for one to four series that should be read precisely against a y axis.

Installation

Pro source and install commands unlock with a Pro plan.

Usage

Render <HorizonChart /> for the demo, or pass series (signed values against a baseline, one per day), days and ticks, plus baselineLabel for the legend. defaultSort, defaultBands and extent set the opening view; onSortChange, onBandsChange and onDayChange report changes.

example.tsx
1import { HorizonChart } from "@/components/arc/blocks/horizon-chart/horizon-chart";2 3export function MarketHealth({ markets, days }) {4  return (5    <HorizonChart6      title="Sessions against baseline"7      series={markets}8      days={days}9      baselineLabel="spring average"10      defaultBands={3}11      onDayChange={index => setInspectedDay(index)}12    />13  );14}
Built fromSegmented control

API reference

HorizonChart

A horizon chart that stacks many daily series as compact rows. Each row folds its curve into bands of rising intensity, one hue above the baseline and one below, so a dozen series fit in the height of two line charts. A synced crosshair reads every row on the same day, rows sort by name, peak swing or latest value on one spring, a band count setting folds and unfolds every row together, and a legend explains how to read the bands.

PropTypeDefaultDescription
seriesHorizonSeries[]horizonSeriesOne row per series: an id, a name and one value per day as a signed distance from its baseline (12 means 12% above, -8 means 8% below).
daysHorizonDay[]horizonDaysDays on the shared x axis, oldest first, each with a short label and a long label for the readout and screen readers.
ticks{ index: number; label: string }[]horizonMonthTicksLabels on the x axis, each placed at a day index.
titlestring"Sessions against baseline"Heading of the block.
descriptionstringhorizonDescriptionLine under the title. Pass an empty string to hide it.
baselineLabelstring"spring average"What the values are measured against, used in the legend caption and for screen readers.
defaultSort"name" | "peak" | "latest""peak"Opening row order. Peak sorts by the largest swing from baseline in either direction, latest by the value on the last day.
defaultBands1 | 2 | 3 | 43Opening number of bands each row folds into. One band draws a plain area chart.
extentnumber–Distance from the baseline that fills every band; larger values stay in the strongest band. Defaults to the largest value rounded up to a multiple of 12, so every band count has a round step.
formatValue(value: number) => stringsigned whole percentFormats a value for the rows, the legend and screen readers. The default uses a true minus sign.
onSortChange(sort: HorizonSort) => void–Called after the row order changes.
onBandsChange(bands: 1 | 2 | 3 | 4) => void–Called after the band count changes.
onDayChange(index: number | null) => void–Called when the synced crosshair moves to a day, or with null when it clears.
classNamestring–Class on the root element.

Keyboard interactions

Tab
Moves to the sort switch, the band count switch and the chart.
Arrow keys
On a switch, moves the selection. On the chart, moves the crosshair one day left or right.
Shift+Arrow leftorright
On the chart, moves the crosshair one week.
HomeorEnd
On the chart, jumps to the first or last day.
Escape
On the chart, clears the crosshair and shows the latest day again.

Accessibility

  • The chart is a focusable group with a keyboard hint; focusing it places the crosshair on the latest day.
  • A visually hidden table lists every row with its latest value, its highest and its lowest value with their dates, in the current order.
  • Moving the crosshair by keyboard announces the day with the highest and lowest row through one polite live region; sort and band changes are announced there too.
  • Values carry a sign and the legend names both sides (below and above), so direction never depends on color alone. The color scale is an image with a full text label.
  • Pointer focus draws no ring; keyboard focus shows Arc's shared focus ring. Keyboard position shows as the crosshair, the readout and the selected fills of the switches.

Motion

  • Changing the band count animates one motion value that drives two custom properties on the root, so every layer in every row folds or unfolds together on a no-overshoot spring, on transform and opacity alone.
  • Sorting moves each row to its new place with a layout spring that never overshoots.
  • The crosshair is a full-width track translated by a share of its own width, following the pointer on a short critically damped spring; it appears in place instead of sliding in from its last day.
  • Row values and the date are written straight to the DOM while scrubbing, so the pointer never renders React.
  • Reduced motion makes the fold, the reorder and the crosshair instant.

Responsive behavior

  • Rows share one plot column, so the crosshair spans every row at any width.
  • Below a 560px container the name and value columns narrow, text steps down to the small size and the switches wrap under the title at full width.
  • Switches grow to 44px on touch. Tapping or dragging across the chart sets the crosshair and keeps it after the finger lifts; vertical scrolling is never captured.
  • The legend moves under the readout on narrow screens and keeps its fixed scale so band changes never shift the layout.

Performance

  • Each row draws its above and below areas once as paths in a unit box; the bands are CSS transforms of those paths, so changing the band count never rebuilds geometry.
  • Scrubbing writes the date and one text node per row without React renders; hit testing uses one rectangle over the plot column.
  • Comfortable up to about 40 rows of a few hundred days; beyond that, aggregate days into weeks.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Choose it for 8 to 30 series on one time axis where each value is a deviation from a baseline: sessions against a seasonal average, latency against a target, demand against a forecast, returns against a benchmark.
  • Pass signed values against the baseline, not raw levels. Convert levels first (value / baseline - 1) so rows of different sizes compare on one scale.
  • The above side uses the first chart series color and the below side the third, both theme tokens that follow the accent; keep them if you restyle.
  • Every row always renders four layers per side; a layer above the band count covers values past the extent and stays empty, which is what lets the band count animate continuously.
/components/blocks/horizon-chart/markdown

The full library index for assistants is at /llms.txt.

Related

Small multiplesProBlockA trellis of small line charts that reads as one chart, with a synced crosshair, a shared or independent y scale that springs the axes, sorting with a FLIP reorder, a ghost of the overall total behind every panel and dimming for flat trends.RidgelineComponentOverlapping distributions, one ridge per group: hover to lift a ridge and read its quartiles, switch datasets and every curve morphs.StreamgraphComponentLayered streams on a wiggle baseline that morph between ranges, with a layer you can isolate and read week by week.Activity heatmapComponentSee a year of activity at a glance, one square per day.
PreviousConnected scatterNextLine replay
Arc · 102 block pagesllms.txt

On this page

  • Preview
  • When to use
  • When not to use
  • Installation
  • Usage
  • API reference
  • Keyboard
  • Accessibility
  • Motion
  • Responsive behavior
  • Performance
  • Notes for AI
  • Related
View as Markdown

Sessions against baseline

Daily sessions by market compared with each market's spring average, June to September 2026

Sort
Bands

Sunday, September 27Latest day

−36%Below0Above+36%

Each row folds into 3 bands of 12%. Stronger color means further from the spring average.

  1. Milan+2%
  2. Warsaw+9%
  3. Amsterdam+2%
  4. Paris+4%
  5. Stockholm−2%
  6. Madrid0%
  7. London+7%
  8. Lisbon+3%
  9. Copenhagen−1%
  10. Vienna0%
  11. Prague+13%
  12. Berlin+8%
  13. Zurich−6%
  14. Dublin+4%
JunJulAugSep

Use the left and right arrow keys to move the crosshair by one day, Shift with an arrow for one week, Home and End for the first and last day, and Escape to clear it.

Sessions against baseline, values against the spring average
RowLatestHighestLowest
Milan+2%+2% on Sep 27−36% on Aug 5
Warsaw+9%+14% on Sep 15−35% on Aug 18
Amsterdam+2%+35% on Sep 8−3% on Jun 21
Paris+4%+7% on Sep 21−33% on Aug 8
Stockholm−2%+4% on Aug 12−32% on Jun 19
Madrid0%+3% on Jun 5−31% on Aug 20
London+7%+31% on Jul 6−3% on Jun 27
Lisbon+3%+5% on Jul 11−26% on Aug 3
Copenhagen−1%+4% on Aug 3−22% on Jul 4
Vienna0%+3% on Jul 15−18% on Aug 9
Prague+13%+17% on Sep 24−5% on Jul 19
Berlin+8%+10% on Sep 11−5% on Jun 13
Zurich−6%+3% on Jun 12−8% on Sep 20
Dublin+4%+5% on Aug 14−5% on Sep 12