Skip to main content

Marking up charts: annotations and indicators

You will learn
  • The three annotation primitives: Region, Marker, Baseline
  • Why annotations render in a deliberately different register than data
  • YAxisIndicator — a live value pill pinned to an axis edge

Everything drawn so far has been data — a series, read straight off a column. This chapter is about marks you place: an incident window, a deploy instant, a target line.

src/examples/learn-07-annotations.tsx
import {
Baseline,
ChartContainer,
ChartRow,
Layers,
LineChart,
Marker,
Region,
YAxis,
YAxisIndicator,
} from '@pond-ts/charts';
import { useSiteChartTheme } from '@site/src/theme/useSiteChartTheme';
import { singleHostSeries } from './lib/server-metrics';

const STEP_MS = 60_000;

export default function LearnAnnotations() {
const theme = useSiteChartTheme();
const series = singleHostSeries();
// singleHostSeries() always returns a non-empty, fixed-length series, so
// timeRange() is never undefined here.
const base = series.timeRange()!.begin();
const latest = series.at(series.length - 1)?.get('cpu');

return (
<ChartContainer range={series.timeRange()} width={560} theme={theme}>
<ChartRow height={220}>
<YAxis id="pct" side="right" format=".0%" />
<Layers>
<LineChart series={series} column="cpu" axis="pct" />
<Region
from={base + 40 * STEP_MS}
to={base + 55 * STEP_MS}
label="busy"
/>
<Marker at={base + 40 * STEP_MS} label="deploy" />
<Baseline value={0.4} axis="pct" label="target" />
{latest !== undefined && <YAxisIndicator value={latest} axis="pct" />}
</Layers>
</ChartRow>
</ChartContainer>
);
}
<Layers>
<LineChart series={series} column="cpu" axis="pct" />
<Region from={busyStart} to={busyEnd} label="busy" />
<Marker at={deployInstant} label="deploy" />
<Baseline value={0.4} axis="pct" label="target" />
<YAxisIndicator value={latestCpu} axis="pct" />
</Layers>
  • <Region from={} to={}> — a shaded x-span. An incident window, a trading session, any range you want to call out.
  • <Marker at={}> — a vertical line at one x. A deploy, an alert fire, any instant.
  • <Baseline value={}> — a horizontal line at one y. A target, a threshold, an SLA.
  • <YAxisIndicator value={}> — not an annotation on the plot; a pinned pill at the axis edge showing one live number. Chapter 8 pairs it with a source prop for the high-frequency case — a live price tag that updates without re-rendering the whole chart.

All four live inside <Layers>, alongside the data layers — annotations are just more things in the z-stack, drawn in the order you write them (chapter 2).

Two registers, never mixed

Every annotation renders in the theme's dedicated annotation colour — a hue deliberately different from every data role (primary, secondary, and the rest). The rule: data and the marks you place never share a hue family, so a placed mark is never mistaken for a series at a glance, and a series is never mistaken for something you annotated. Look back at the chart above — the cpu line and the three marks read as two unmistakably different registers, not four colours competing for attention.

Recap

Region/Marker/Baseline are the three ways to mark a chart up — a span, an instant, a threshold — plus YAxisIndicator for a pinned live value. All four are Layers children like any draw layer. Selection, depth, and interactively editing or creating marks are deeper topics than this chapter covers — see the Charts/Annotations Storybook group for that ground today; a dedicated reference section is on the roadmap.

Next: Live charts — the running example, finally live.