PitchKit
Styling

Theming

CSS variables only — set once, dark mode for free, override per chart.

PitchKit's theming is CSS variables only — the same mechanism shadcn/ui uses internally. There are no JS theme objects and no per-instance colour props to thread through: set a variable once in your global stylesheet and every <Pitch> picks it up through the cascade.

The variables

VariableControlsDefault
--pitch-surfaceGrass fill#1a472a
--pitch-stripeMow-stripe overlayrgba(255, 255, 255, 0.04)
--pitch-linesPitch markings; <Annotate> text; <Voronoi> cell edgesrgba(255, 255, 255, 0.8)
--pitch-line-widthMarking stroke width1.5
--pitch-marker-primaryDefault mark colour (<Scatter>, <Arrows>, <Comet>…)#3b82f6
--pitch-marker-goal<GoalAngle> wedge fill#f97316
--pitch-tooltip-bgTooltip backgroundrgba(17, 17, 17, 0.92)
--pitch-tooltip-colorTooltip text#fff

Goal view variables

<GoalView> reads three more, and draws its ground and markings with the pitch's own --pitch-surface, --pitch-lines and --pitch-line-width, so a themed pitch and goal view match.

VariableControlsDefault
--pitch-goal-backdropEverything behind the goal, the label boxes and the ring on shots#0f2a19
--pitch-goal-netThe netrgba(255, 255, 255, 0.18)
--pitch-goal-framePosts and crossbar#ffffff

Player avatar variables

<PlayerAvatar> reads twelve more, and draws its cards with the --pitch-card-yellow and --pitch-card-red below.

VariableControlsDefault
--pitch-avatar-sizePhoto diameterScales with the pitch
--pitch-avatar-bgBehind the photo and the initials#d4d8dd
--pitch-avatar-ringThe ring around the photo#ffffff
--pitch-avatar-textThe name#ffffff
--pitch-avatar-mutedThe shirt numberrgba(255, 255, 255, 0.7)
--pitch-avatar-iconThe goal and assist icons and counts#111827
--pitch-avatar-icon-haloThe thin edge that lifts icons off the grassrgba(255, 255, 255, 0.9)
--pitch-avatar-ballThe ball's light panels#ffffff
--pitch-avatar-initialsThe initials when there is no photo#1f2937
--pitch-sub-onThe square, for a player who came on#22c55e
--pitch-sub-offThe square, for a player who went off#ef4444
--pitch-sub-arrowThe arrow inside the square#ffffff

Chart variables

The charts read a second set. They are not the pitch's colours: a chart has no pitch, so these are meant to match the surface behind it, and most apps will set a light and a dark value.

VariableControlsDefault
--pitch-series-1 … -6Series lines and bars, by position. <MomentumChart> uses 1 (home) and 2 (away)#3b82f6, #eb6834, #1baf7a, #eda100, #e87ba4, #008300
--pitch-chart-surfaceThe ring around markers and the backing behind icons; set it to whatever is behind the chart#ffffff
--pitch-chart-textValues, labels, legends#12170f
--pitch-chart-mutedTick labels#7b8474
--pitch-chart-accentControls the chart draws, such as the radar's Back button#15803d
--pitch-chart-accent-textText on those controls#ffffff
--pitch-axisBaselines, period rules, the zero line#c6cebc
--pitch-gridGridlines and panel backgrounds#e5eade
--pitch-card-yellowYellow cards on a chart and on <PlayerAvatar>#facc15
--pitch-card-redRed cards and own goals on a chart, and red cards on <PlayerAvatar>#ef4444

Charts that overlay series, like the radar, read best with up to three: past that, no palette keeps every pair distinguishable.

A yellow card is under 1.5:1 against white, so on a light surface darken --pitch-card-yellow. Each chart's page lists the variables it reads.

/* globals.css */
:root {
  --pitch-surface: #1a472a;
  --pitch-lines: rgba(255, 255, 255, 0.8);
  --pitch-marker-primary: #3b82f6;
}

Every variable has a built-in fallback, so nothing is required — an unthemed <Pitch> still renders sensibly.

Dark mode

Because it's plain CSS, dark mode is a second override behind whatever convention your app already uses — a class, a media query, a data-theme attribute:

.dark {
  --pitch-surface: #0b1712;
  --pitch-lines: rgba(255, 255, 255, 0.35);
}

No PitchKit configuration involved. This site's own examples work exactly this way.

Per-chart overrides

Variables cascade, so scoping an override to one chart is just a wrapper element:

<div style={{ "--pitch-surface": "#101418" } as React.CSSProperties}>
  <Pitch type="statsbomb">{/* this one renders dark */}</Pitch>
</div>

Structure vs colour

The appearance prop is deliberately separate from theming: it toggles which shapes get painted (grass stripes, goal style), never colours.

<Pitch type="statsbomb" appearance={{ stripes: true, goalType: "box" }} />

Individual marks

Colour accessors (fill, stroke, color) always win over the themed default for the marks you pass them to — theming sets the baseline, accessors express data. For styling marks with utility classes instead, see Tailwind, which covers className, the data-pitchkit-* attribute escape hatch, and the @utility recipe for the pitch background.

On this page