SpiralYear
React spiral year chart — a full year of values wound as a spiral by day or week. Tiny accessible SVG microchart; RSC-safe static or /interactive.
SpiralYear answers "how did the year breathe?" It winds a calendar series onto an Archimedean spiral: the angle is the
position in the year (January at 12 o'clock, running clockwise) and each turn outward is the next year. The value
becomes a five-step opacity, so busy stretches read as darker arcs of the spiral. This is a pattern instrument —
opacity is the weakest ordered channel, so for an exact day's value reach for ActivityGrid or HeatStrip.
Install
import { SpiralYear } from "@microcharts/react/spiral-year";// byWeek — real values under “Sample data” below<SpiralYear data={byWeek} title="Seasonality" />Sample data
// 52 weekly values, a summer peak in week 30const byWeek = [ 85, 95, 107, 120, 135, 150, 166, 183, 200, 217, 234, 250, 265, 280, 293, 305, 315, 324, 331, 336, 339, 340, 339, 336, 331, 324, 315, 305, 293, 480, 265, 250, 234, 217, 200, 183, 166, 150, 135, 120, 107, 95, 85, 76, 69, 64, 61, 60, 61, 64, 69, 76,];Try it
When to use it
- Good for — the seasonal shape of a year at a glance, spotting a busy season or a quiet stretch, or a compact "the year in one square".
- Avoid for — reading an exact day's value (
ActivityGrid/HeatStrip), a non-cyclic trend (Sparkline), or more than about three years.
Variants
locale doesn't change any in-chart mark — opacity steps carry the value, never a printed number — but it does localize
the peak and low values named in the accessible summary:
Sizing
Edge cases
Four homes
Why this default
Month ticks are on by default because without a temporal anchor the spiral is just texture — the ticks are what make
"when" readable. Five steps is the ceiling: ordered opacity supports about five discriminable levels, and more would be
false precision. The spiral radius encodes time only, never value — an outer mark is simply a later date, not a
bigger number. Two adjacent turns place different years at the same angle; that radial adjacency is calendar alignment,
not similarity, and the chart never asks you to compare across it. A null day leaves a gap in the spiral, distinct
from a faint step-one mark, because missing is not the same as low.
Accessibility
The accessible name states the count, the peak, and the low — "52 weeks; peak 1.480 in week 30, low in week 48."
(the de-DE localized demo). The interactive entry lets you arrow along the spiral chronologically, each mark announced
with its period and value through a polite live region. Because the channel is ordinal opacity, the docs and summary
steer any exact read to ActivityGrid.
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)[] | One value per day or week (cadence inferred from length). |
| cadence | "day" | "week" | Data cadence; inferred from length (≈52 → week, else day) when omitted. |
| startDate | string | ISO date anchoring index 0 to a calendar angle. |
| steps | 3 | 5 | Opacity quantization (default 5). |
| monthTicks | boolean | Faint radial month ticks (default true). |
| mark | "dot" | "arc" | Dots (default) or short arc segments. |
| 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.
Related charts
PolarClock
React polar clock chart — cyclic magnitudes around a clock-like ring. Tiny accessible SVG microchart; RSC-safe static or /interactive.
BreathingDot
React breathing dot chart — ambient load as pulse rate, with a static reduced-motion twin. Tiny accessible SVG microchart; RSC-safe static or /interactive.