Empty states keep the component shell; only the plot region goes blank
`<ShotMap shots={[]} />` renders the header stats (with zeroed values), a muted pitch, and a centred "No shots" message. Frame and chrome stay visible. The shell preserves layout during async loads and clearly distinguishes "no data" from "component not rendered".
component-defaultsaccessibilityapi
Context
Three common ways to handle zero data:
- Render nothing / null — component disappears. Layout reflows around a hole. Consumers wrap every usage in a conditional.
- Render a placeholder skeleton — conveys loading, not emptiness. Misleads when the data is genuinely empty.
- Render the shell with an empty-plot message — honest, stable, no consumer conditional required.
Decision
Every Campos chart renders its shell (header, frame, chrome) on empty input. The plot region mutes, the empty-state message centres, and dependent legends / scale bars hide. Header stats reduce to zero where applicable (e.g. “0 shots”, not hidden).
The emptyState semantic region is always present in the compute model
even when the plot is populated — compute detects emptiness and returns a
populated emptyState object for renderers to consume.
Consequences
- Async data flows don’t need conditional wrappers. Layout stays stable during load → empty → populated transitions.
- Loading states and empty states look different by default (loading is typically a skeleton shell from the consumer; empty is the muted chart with the message).
- Empty-state copy is overridable per chart via an
emptyState.messageprop; default copy is editorial (“No shots”, “No passes”, etc.). - Static export serialises the empty shell identically to a populated chart — no render-layer conditionals.