Goal
Reusable goal-mouth surface for component authors: striker or goalkeeper perspective, goal-space projection, frame, net, and ground styling, theme and color overrides, accessibility hooks, and export-safe SVG composition.
Canonical behaviors.
Shot placement built directly on <Goal>. The Usage example below shows the component boundary; layer your own markers, legend, and surrounding chart UI outside the primitive.
The same Goal shot map can be viewed from striker or goalkeeper perspective. The projection mirrors horizontally; the marker data and component usage stay the same.
Shared concerns.
Accessibility
Use role="img" and ariaLabel when Goal is the standalone
surface. Higher-level chart components usually own the full chart label, legend, and
surrounding explanatory text.
Sizing
Goal is much shallower than Pitch, so docs and chart containers should usually constrain width rather than stretching it across a very wide card.
Export
Goal is deterministic SVG. Keep interactive={false} for export snapshots
or when a parent owns interaction.
Composition
Goal owns only the frame, ground line, net, and projection. Titles, legends, summary metrics, and provider parsing should stay outside the primitive.
Best-practice examples.
Goal
Goal-mouth surface usage. Keep provider parsing outside the component, set frame/net styling at the surface boundary, and project 0..100 goal-mouth coordinates inside the render prop.
import { Goal } from "@withqwerty/campos-stadia";
type GoalPoint = {
id: string;
goalMouthY: number;
goalMouthZ: number;
color: string;
};
export function GoalMouthSurface({
shots,
exportMode = false,
}: {
shots: readonly GoalPoint[];
exportMode?: boolean;
}) {
return (
<Goal
facing="striker"
interactive={!exportMode}
role="img"
ariaLabel="Goal-mouth placement"
>
{({ project }) =>
shots.map((shot) => {
const point = project(shot.goalMouthY, shot.goalMouthZ);
return <circle key={shot.id} cx={point.x} cy={point.y} r={0.08} fill={shot.color} />;
})
}
</Goal>
);
}
Public surface.
Goal-mouth surface contract for shot-placement and goalkeeping views.
| Prop | Type | Default | Description |
|---|---|---|---|
facing | "striker" | "goalkeeper" | required | Goal-mouth perspective. Striker keeps left post on screen-left; goalkeeper mirrors horizontally. |
theme | "primary" | "secondary" | "primary" | Built-in goal color preset. Colors override the selected preset. |
colors | GoalColors | - | Optional frame, net, ground, and background overrides for export frames, dark contexts, or editorial treatments. |
netStyle | "none" | "light" | "dense" | "light" | Net rendering density. none leaves just the frame and ground line. |
netShape | "flat" | "box" | "box" | Net geometry. box draws a recessed broadcast-style back frame and side panels inside the mouth. |
netBackInset / netBackOffsetTop / netBackOffsetBottom | number | surface-scaled | Controls recessed net depth and perspective when netShape='box'. Use these for broadcast-style 3D goal-mouth treatments. |
netOpacity / netColumns / netRows | number | 0.45 / 8 / 4 | Controls net visibility and grid density. |
barThickness / netThickness / groundThickness | number | surface-scaled | Independent stroke widths, in SVG user units. Changing bar thickness does not force net or ground thickness. |
padding | number | { top; right; bottom; left } | 0 | Extra viewBox breathing room, expressed as a percentage of goal width. |
interactive / role / ariaLabel | boolean / string / string | true / - / - | Static-export and standalone SVG accessibility hooks. |
children | ({ project }) => ReactNode | required | Foreground SVG marks. The project callback maps goal-mouth Y/Z 0..100 coordinates to meter-scale SVG user units. |
Projection And Geometry Helpers
Framework-neutral helpers for goal-space rendering and export tooling.
createGoalProjection(facing) Framework-neutral projection from goal-mouth Y/Z coordinates to SVG meter units.
computeViewBox("goal", "vertical", padding?) Returns the SVG viewport for the goal frame.
computeGoalMarkings() Framework-neutral goal geometry arrays for non-React renderers. React consumers should prefer <Goal>.
Create a React component using the Goal primitive from @withqwerty/campos-stadia. Keep provider parsing outside the component, project goal-mouth Y/Z coordinates inside the render prop, and include role='img', ariaLabel, and interactive={false} where the surface is standalone or export-oriented.