Semiotic accounts for hierarchy and placement in how it renders annotations but it also provides support for the failure states when a chart has more than a couple of notes: there are too many of them, and some of them tie to their target by color alone.
A density budget, with a floor
Past a certain count, more annotation is less communication. Semiotic has an opt-in density pass: it derives a budget from the plot area (roughly one note per 20,000 px², or an explicit maxAnnotations cap) and sheds the lowest-priority notes when the chart exceeds it. Priority is not invented for this feature--it reuses the signals already on the annotation: emphasis first (a primary note is the floor and is never shed), then provenance.confidence, then lifecycle.freshness(an expired note goes first). Reference lines, bands, and statistical overlays never count toward the budget.
Keep the primary note and sheds the lowest-priority labels once the plot is over its budget. The primary note is never dropped.
The switch lives inside the same placement config, because density only makes sense after you know where things land:
<Scatterplot data={data} xAccessor="x" yAccessor="y" autoPlaceAnnotations={{ density: { maxAnnotations: 3 } }} annotations={[ { type: "callout", pointId: "peak", label: "Main claim", emphasis: "primary" }, { type: "label", pointId: "a", label: "Note A" }, { type: "label", pointId: "b", label: "Note B" }, // ...lowest-priority notes shed first ]} />"Hover is not guaranteed," so dropping notes outright is the default. But when an interaction surface exists, progressiveDisclosure: true keeps the shed notes in the DOM, hidden until the chart is hovered or focused, and reveals them then. The persistent set is always rendered so that a non-hover reader still receives them. The raw split is also available as a pure function:
import { annotationDensity, annotationBudget } from "semiotic/recipes" const { visible, deferred, budget } = annotationDensity({ annotations, width: 600, height: 400, })And the diagnostic side: diagnoseConfig raises an advisoryANNOTATION_DENSITY warning when note count exceeds the same budget.
Association you can actually follow
The correspondence problem is the failure mode when a note connects to its target by color, and the reader can't make the match because the colors aren't discriminable, or because the reader is color-blind, or because the reader is using a screen reader and never sees color at all. Labels and callouts already draw a connector; enclosures wrap their subject; reference lines span the plot. The gap is the text note, which draws no connector. For instance, a colored, offset text note is the classic color-only case.
Give a colored, offset text note a faint leader line back to its anchor to provide a spatial cue a color-blind reader can follow, instead of relying on hue matching.
For this, we have the audit itself: auditAccessibility emitsperceivable.annotation-association, a warning when a colored note has no connector, enclosure, or reference-line cue, distilled into capability caveats through the existing accessibilityCaveats path. There is also the default behavior viaautoPlaceAnnotations: { redundantCues: true } that gives the coloredtext note a faint leader line from the note back to its anchor. The cue isspatial, not another color, so it survives color-blindness. The audit treatsredundantCues as satisfying the check.
<Scatterplot data={data} xAccessor="x" yAccessor="y" autoPlaceAnnotations={{ redundantCues: true }} annotations={[ { type: "text", pointId: "b", label: "Echoes the series color", color: "#d7263d", dx: 44, dy: -30 }, ]} />Connector-necessity, the inverse smell, was already covered: diagnoseConfigflags a far note with no connector (ANNOTATION_FAR_NO_CONNECTOR) and a very long connector whose target could have been adjacent (ANNOTATION_LONG_CONNECTOR) reflect the research position to prefer adjacency and only use connectors that isn't possible.
Why these two belong together
Dealing with note density and connector behavior are both about the annotation layer staying legible under load. Density keeps the layer from drowning the marks; association keeps each surviving note tied to the thing it describes, for every reader. Together with hierarchy and placement, they move Semiotic's annotations toward a larger goal: a communicative layer engineered with the same care as the data marks. That requires it to not only display nice annotations it also means it should degrade gracefully for the readers who can't rely on color or hover.