accepted

ShotMap renders goals after non-goals so they never get occluded in dense clusters

Markers are sorted by outcome before rendering: non-goals first, goals last. In dense penalty-area clusters, goal markers always sit on top of shot markers. Deterministic and unconditional — no API to override.

ShotMap visual-encodingfootball-semantics

Context

In dense penalty-box clusters, overlapping markers hide each other. SVG paint order follows element order in the DOM — whichever marker is drawn last sits on top. Without an explicit sort, order depends on input order, which is typically chronological (first shot minute 3, last shot minute 89). A 6th-minute goal gets buried under an 86th-minute miss at the same point, and the chart loses its most important read.

Decision

Before rendering, the marker array is sorted so outlineKey === "goal" entries land at the end. SVG element order follows array order within the same layer, so goals paint last. Sort is stable — within the same outcome class, input order is preserved.

Consequences

  • Goal markers are visually guaranteed in dense clusters. The chart’s primary editorial read survives overlap.
  • Z-order is not user-configurable. Consumers whose workflow demands chronological z-ordering construct custom markers via markers.show and markers.shape themselves; the default refuses to bury goals.
  • Hover / keyboard navigation can still reach hidden non-goals under a goal marker — interaction ordering follows the accessibility tree, not the paint order.
  • Tests assert the sort explicitly rather than relying on input order, so future refactors can’t regress the invariant silently.
← All decisions