Radial Seasonality
A radial clock that loops a cyclic metric around the year, with a prior-cycle ghost ring and the peak period marked — reading the seasonal shape at a glance.
Acme · MRR seasonality
5.2k
peak · Nov 26
- This year
- Last year
Peaks in Nov 26 at 5.2k, 14% above the 12-period average and the year is up 24% vs last; the low is Jan 26 (4.2k).
| Period | This year | Last year |
|---|---|---|
| Jan 26 | 4.2k | 3.4k |
| Feb 26 | 4.2k | 3.4k |
| Mar 26 | 4.4k | 3.5k |
| Apr 26 | 4.5k | 3.6k |
| May 26 | 4.7k | 3.8k |
| Jun 26 | 4.4k | 3.5k |
| Jul 26 | 4.3k | 3.5k |
| Aug 26 | 4.4k | 3.5k |
| Sep 26 | 4.7k | 3.8k |
| Oct 26 | 4.9k | 4k |
| Nov 26 | 5.2k | 4.2k |
| Dec 26 | 4.9k | 3.9k |
Acme analytics · updated daily at 12am UTC
Demo data is illustrative. Replace with your own typed data prop.
Use it with your data
Data contract
Passed via the data prop — an object: validated at runtime, so a bad shape degrades to the error state rather than crashing.
| Field | Type | Required |
|---|---|---|
| periods | array | yes |
| current | array | yes |
| prior | array | no |
Static props
import { ChartRadialSeasonality } from "@/components/blocks/chart-radial-seasonality/chart-radial-seasonality";
<ChartRadialSeasonality data={myData} />Client fetch (SWR)
const { data, isLoading, error } = useSWR("/api/metric", fetcher);
// route isLoading/error through <ChartStates>, then:
<ChartRadialSeasonality data={data} />Server component (RSC)
const data = await fetch(url, {
next: { revalidate: 3600 },
}).then((r) => r.json());
<ChartRadialSeasonality data={data} />Map arbitrary rows
import { mapRows } from "@/lib/sextant/column-map";
const out = mapRows(rows, { /* contractKey: "yourColumn" */ }, dataSchema);
if (out.ok) <ChartRadialSeasonality data={out.data} />;Make it yours
Every customization below is an optional prop — omit them all and you get the defaults shown above. Recolor, reformat, restyle, or drop the card chrome without forking the component.
Recolor or rename a series (current, prior)
<ChartRadialSeasonality
series={{ current: { color: "var(--chart-3)", label: "…" } }}
/>Format numbers (currency / locale)
const eur = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
notation: "compact",
});
<ChartRadialSeasonality valueFormatter={(n) => eur.format(n)} />Replace or hide the narrative
<ChartRadialSeasonality narrative="Your own one-liner." />
// …or hide it entirely:
<ChartRadialSeasonality narrative={false} />Restyle any region
<ChartRadialSeasonality
className="max-w-xl"
classNames={{ body: "bg-muted/20" }}
/>Drop the card chrome (headless plot)
import { ChartRadialSeasonalityPlot } from "@/components/blocks/chart-radial-seasonality/chart-radial-seasonality";
// Just the chart body — bring your own card/layout:
<ChartRadialSeasonalityPlot data={data} config={config} />