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.
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.showandmarkers.shapethemselves; 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.