Semiotic's annotation layer is well-placed, un-crowded, legibly associated to its target, responsive to space and reader, and defensible when the chart travels. That's all about display. But annotations are durable artifacts of meaning and that requires a way to treat them not just as marks but also as units of conversation: a note you canargue about, and a note a non-visual reader can actually receive.
The editorial half of the lifecycle
Semiotic already modeled the temporal lifecycle (does this note still apply as the data ages (freshness)?) but we also need to treat the orthogonaleditorial lifecycle. Is this note still believed? The two are independent: a note can be fresh-but-disputed or stale-but-accepted. Status moves through a small state machine through proposed (a watcher placed it, unreviewed) → accepted /disputed → retracted and finally supersedes links a revision to the note it replaces.
applyAnnotationStatus turns that standing into a default visual treatment, the editorial companion to applyAnnotationLifecycle's temporal one. Crucially the opacity multiplies in, so freshness, importance (emphasis), and status compose as three independent dims of one render.
Four notes with four editorial states. The disputed note wears a (?), the proposed watcher note reads provisionally, the retracted note is gone, and the accepted note paints at full weight.
import { applyAnnotationStatus, applyAnnotationLifecycle } from "semiotic/ai" // Temporal first, then editorial. const treated = applyAnnotationStatus( applyAnnotationLifecycle(annotations, { dataExtent }) )It also resolves supersession (a note replaced by a present, non-retracted revision is hidden), and the transitions are observable:recordAnnotationStatusChange("disputed", { annotationId, fromStatus })drops an event into the conversation arc. That's the point where an annotation stops being chart decoration and becomes the durable node the arc is about.
The natural-language bridge
An author-placed annotation is intent in its purest form. It should be the first thing a reader who can't see the chart hears, not an afterthought. That's why Semiotic wires annotations into the two surfaces that speak to non-visual and agent readers.
First, the description:describeChartnow leads with the author's notes: "The author has marked 2 features on this chart: a callout labeled 'Quarter-end peak', and an AI-suggested threshold line labeled 'Target'." . It does this ahead of the encoding, statistics, and trend. Provenance shapes the phrasing: an agent- or watcher-authored note reads as such.
Second, the navigation tree:buildNavigationTreeadds an Annotations branch, so a screen-reader user encounters the notes while traversing the structure. This is the same way we expose the axes and series. Each node reuses the description's vocabulary and surfaces its editorial status inline.
Expand Annotations to hear the notes the way a screen-reader user would with provenance and status read aloud.
- ▾ The author has marked 3 features on this chart: a callout labeled "Quarter-end peak", an AI-suggested threshold line labeled "Target", and a callout labeled "Spike contested". A line chart of sales by month. sales ranges from 4,200 (Jan) to 7,100 (Jun), with a mean of 5,500 across 6 points. Overall sales rises from 4,200 (Jan) to a peak of 7,100 (Jun). This is an alerting chart; the climb to 7,100 at Jun warrants a closer look.
- X axis: month, Jan to Jun (6 points).
- Value axis: sales, 4,200 to 7,100.
- Jan: 4,200
- Feb: 5,100
- Mar: 6,800
- Apr: 4,600
- May: 5,200
- Jun: 7,100
- ▸ Annotations: 3 marked features.
buildNavigationTree("LineChart", { data, xAccessor, yAccessor, annotations }) // → root // ├ X axis / Value axis / data points… // └ Annotations: 3 marked features. // ├ A callout labeled "Quarter-end peak". // ├ An AI-suggested threshold line labeled "Target". // └ A callout labeled "Spike contested" (disputed).It works on every chart family, including network, hierarchy, and geo charts that otherwise return a root-only node. They still get their annotations branch, so the author's intent is always reachable.
Contested, and heard
Every annotation needs to address two halves of the same question: does this note still carry weight, and does it reach the reader? Editorial status makes a note something a team can dispute, supersede, and retract, with every move recorded. Making them accessible makes sure that whatever survives that process is delivered to the readers who can't rely on pixels. Together they make annotations into first-class, data-bound, well-crafted, audience-aware, contestable, and receivable objects. Annotations should be the conversational substrate and meaning layer of a chart, not just decoration.