Primitive

Pitch

Reusable football pitch surface for component authors: crop and orientation, pitch-space projection, theme and color overrides, grass patterns, tactical overlays, underlay layering, accessibility hooks, and export-safe SVG composition.

Playground

Interactive props.

Attacking direction
Crop
Frame
Theme
Side
Zones
Stories

Canonical behaviors.

Viewport Modes

Same crop, two outer SVG contracts. frame='crop' keeps the viewport tight to the focused half; frame='full' keeps a full-pitch viewport around the same cropped surface.

Tight viewport
frame="crop"
Full viewport
frame="full"
Horizontal Orientation

Half-pitch surfaces can be rendered left-to-right for components that scan horizontally or need a wide layout.

Underlay Layering

The left block is rendered through underlay, so the pitch lines sit on top of it. The right block is rendered in children, so it sits above the pitch lines.

Underlay block sits below pitch linesForeground block sits above pitch lines
Theme Overrides

Pitch is easy to retheme. Use the controller to preview the built-in presets and a custom color override without changing the component structure.

Built-in themes handle the base surface. colors can then override fill, lines, and tactical markings for custom palettes.

Static Export

The interactive surface can host real popovers above the SVG. interactive=false removes that layer and leaves a clean export-safe snapshot.

interactive=true
Shot
x=88, y=44
Chance
value=0.56
interactive=false
Grass Patterns

Interactive story for built-in mowing patterns. Built-in object patterns are safe to document and serialize; function patterns need an export runtime that owns code execution.

Pattern
Theme
Attacking direction
Crop
Cross-cutting

Shared concerns.

Accessibility

Use role="img" and ariaLabel when Pitch is the standalone surface. Higher-level chart components usually own the full chart label, legend, tooltip, and keyboard semantics.

Responsive

The SVG fills the parent width and preserves the selected frame aspect ratio. Parent layout owns max width. Horizontal stories need wide containers; vertical surfaces scale cleanly into smaller cells.

Export

Pitch is deterministic SVG. Keep interactive={false} for export snapshots, and avoid function-based grass patterns unless the export runtime explicitly supports them.

Themeability

theme gives you a stable built-in preset. colors lets you override fill, lines, and tactical markings directly, which is the right path for brand palettes, blueprint treatments, and dark editorial surfaces.

Composition

Pitch owns only the surface. It does not own provider parsing, legends, tooltips, headers, or chart card layout. Use underlay for background surfaces and children for foreground marks.

Responsive

Normal, small, smallest.

Pitch-based charts scale fluidly to whatever width the parent gives them. The 'smallest' cell is sized for a 5×4 small-multiples grid (~140px wide). Vertical orientation only — horizontal pitches are too wide for small-multiples.

Normal
Small
Smallest (small multiples)
Usage

Best-practice examples.

Pitch

Minimal component-level usage for a pitch-based surface. Use children for foreground marks and add export/accessibility props at the surface boundary.

                    import { Pitch } from "@withqwerty/campos-stadia";
import type { PitchColors } from "@withqwerty/campos-stadia";

type EventPoint = {
  id: string;
  x: number;
  y: number;
  value: number;
};

const pitchColors: PitchColors = {
  fill: "#17462a",
  lines: "#ffffffd9",
  markings: "#ffffff66",
};

export function AttackingHalfSurface({
  events,
  exportMode = false,
}: {
  events: readonly EventPoint[];
  exportMode?: boolean;
}) {
  return (
    <Pitch
      crop="half"
      attackingDirection="up"
      side="attack"
      frame="crop"
      colors={pitchColors}
      grass={{ type: "stripes", opacity: 0.35 }}
      markings={{ halfSpaces: true, thirds: true }}
      interactive={!exportMode}
      role="img"
      ariaLabel={`${events.length} attacking-half events`}
    >
      {({ project }) =>
        events.map((event) => {
          const point = project(event.x, event.y);
          return (
            <circle
              key={event.id}
              cx={point.x}
              cy={point.y}
              r={1.2 + event.value * 2}
              fill="#f6c945"
              stroke="#141414"
              strokeWidth={0.25}
            />
          );
        })
      }
    </Pitch>
  );
}
                  
API

Public surface.

Primary pitch surface contract for pitch-based components, primitives, and static previews.

Prop Type Default Description
crop "full" | "half" | "penalty-area" required Visible pitch surface. Projection still maps the full Campos 0..100 pitch; the crop changes the viewport and rendered markings.
orientation "vertical" | "horizontal" "vertical" Pitch orientation. Vertical attacks upward; horizontal attacks rightward.
side "attack" | "defend" "attack" Visible end for cropped surfaces. Attack is top/right; defend is bottom/left. Ignored for crop='full'.
frame "crop" | "full" "crop" Outer SVG frame. Use crop for tight chart surfaces; use full to keep a full-pitch viewport around the selected crop.
theme "primary" | "secondary" "primary" Built-in pitch color preset. Colors override the selected preset.
colors PitchColors - Optional fill, line, and tactical-marking color overrides. Useful for team palettes, dark surfaces, and export frames.
grass GrassPattern - Optional mowing pattern beneath pitch lines. Built-in object patterns are SVG-safe; formula/custom function patterns need export-pipeline support before serializing.
markings PitchMarkingsConfig - Optional tactical overlays: half-spaces, thirds, 18-zone grid, or 20-zone positional-play grid.
underlay ({ project }) => ReactNode - Layer rendered after the pitch fill and before pitch lines. Use for heatmaps, density surfaces, territory zones, and other backgrounds.
children ({ project }) => ReactNode required Foreground SVG marks. The project callback maps Campos pitch coordinates to meter-scale SVG user units.
padding number | { top; right; bottom; left } 0 Extra viewBox breathing room, expressed as a percentage of pitch width. Use when large marks would clip near the boundary.
interactive boolean true Controls pointer events on the outer SVG. Set false for static export snapshots or when a parent owns interaction.
role string - Optional ARIA role on the outer SVG. Use role='img' for standalone surfaces.
ariaLabel string - Standalone SVG accessibility label. Higher-level chart components usually own the full chart label.

Projection And Geometry Helpers

Framework-neutral helpers for pitch-space rendering and export tooling.

createPitchProjection(crop, orientation)

Framework-neutral projection from Campos pitch coordinates to SVG meter units. Crop is accepted for API symmetry; projection always covers the full pitch.

computeViewBox(crop, orientation, side?, padding?)

Returns the SVG viewport for full, half, and penalty-area pitch surfaces.

computePitchMarkings()

Framework-neutral pitch geometry for non-React renderers. React consumers should prefer <Pitch>.

Use with AI
LLM Prompt
Create a React component using the Pitch primitive from @withqwerty/campos-stadia. Keep data fetching outside the component, use children or underlay for SVG layers, and include role='img', ariaLabel, and interactive={false} where the surface is standalone or export-oriented.
Pitch