# Tailwind

Source: https://www.pitchkitjs.com/docs/styling/tailwind

> Setting PitchKit's CSS variables and styling marks with Tailwind utilities.

The pitch's colours come from the `--pitch-*` variables described in
[Theming](/docs/styling/theming). This page covers reaching those variables, and individual
marks, with Tailwind utilities.

## Marks you render yourself: `className`

`<Scatter>`, `<Arrows>`, `<Comet>`, `<Annotate>`, `<Polygon>`, `<ConvexHull>`, `<Voronoi>` and
`<GoalAngle>` fall back to a themed default `fill`/`stroke` — but
that default is applied as inline `style`, and inline style always beats a class at the same
CSS property. So a `className` utility only takes effect once the corresponding accessor prop
is **omitted entirely** — passing both `fill="red"` and a `fill-*` class silently drops the
class.

```tsx
import { Comet, Pitch, Scatter } from "@pitchkit/react";

// StatsBomb coordinates (120 x 80).
const run = { from: { x: 30, y: 20 }, to: { x: 70, y: 55 } };
const shots = [
  { x: 95, y: 35 },
  { x: 101, y: 42 },
  { x: 108, y: 38 },
];

/**
 * `className` reaches a mark you render yourself — but only once the
 * accessor prop it would otherwise fill (`fill`/`stroke`/`color`) is
 * omitted. That prop is applied as inline style, and inline style always
 * beats a class at the same property.
 */
export function TailwindClassnameBasic() {
  return (
    <Pitch type="statsbomb">
      <Comet
        data={[run]}
        x={(d) => d.from.x}
        y={(d) => d.from.y}
        x2={(d) => d.to.x}
        y2={(d) => d.to.y}
        className="fill-fuchsia-400"
      />
      <Scatter
        data={shots}
        x={(d) => d.x}
        y={(d) => d.y}
        r={6}
        strokeWidth={1.5}
        className="fill-cyan-300 stroke-white transition-colors hover:fill-cyan-100"
      />
    </Pitch>
  );
}
```

## Marks you don't own: a `data-pitchkit-*` selector

Sometimes you're styling a mark whose JSX isn't yours — inside a wrapped recipe component, for
example. Every mark already carries `data-pitchkit-mark`/`data-pitchkit-layer` attributes with
zero setup, reachable via Tailwind's arbitrary-variant selectors on a wrapping element:

```tsx
import { Arrows, Pitch, Scatter } from "@pitchkit/react";

// StatsBomb coordinates (120 x 80).
const pass = { from: { x: 40, y: 60 }, to: { x: 85, y: 30 } };
const shots = [
  { x: 95, y: 35 },
  { x: 101, y: 42 },
  { x: 108, y: 38 },
];

/**
 * For a mark whose JSX you don't own (e.g. inside a shadcn recipe
 * wrapping `<Pitch>`), reach it via the `data-pitchkit-mark` attribute
 * every mark already carries — no `className` prop needed on the mark
 * itself. The trailing `!` is required: these marks never got a
 * `className` to signal an opt-out, so their themed default is still an
 * inline style, which only an `!important` utility can out-rank.
 */
export function TailwindAttributeBasic() {
  return (
    <div className="[&_[data-pitchkit-mark=arrow-head]]:fill-amber-400! [&_[data-pitchkit-mark=arrow-shaft]]:stroke-amber-400! [&_[data-pitchkit-mark=scatter]]:fill-amber-200!">
      <Pitch type="statsbomb">
        <Arrows
          data={[pass]}
          x={(d) => d.from.x}
          y={(d) => d.from.y}
          x2={(d) => d.to.x}
          y2={(d) => d.to.y}
        />
        <Scatter data={shots} x={(d) => d.x} y={(d) => d.y} r={6} />
      </Pitch>
    </div>
  );
}
```

Note the trailing `!` (Tailwind v4's important modifier) in the class list above — it's
required here specifically. Unlike the `className` mechanism, these marks never got a
`className` to signal an opt-out from their themed default, so an ordinary class can't
out-rank the inline style they still carry.

## The pitch background: a `@utility` recipe

The pitch background (outline, stripes, lines) isn't a mark at all — it's restyled only
through the [`--pitch-*` variables](/docs/styling/theming), never via `className` on the shapes directly. Two
ways to set them with Tailwind:

**Zero setup**, using Tailwind's arbitrary-property syntax:

```tsx
<Pitch type="statsbomb" className="[--pitch-surface:var(--color-emerald-950)]" />
```

**Or install this recipe once** (a "shadcn add"-style snippet — copy it into your own
`globals.css`) to get autocompletable, first-class utilities instead, reading any color
already in your Tailwind theme:

```css
/* globals.css */
@utility pitch-surface-* {
  --pitch-surface: --value(--color-*);
}
@utility pitch-stripe-* {
  --pitch-stripe: --value(--color-*);
}
@utility pitch-lines-* {
  --pitch-lines: --value(--color-*);
}
```

```tsx
import { Pitch } from "@pitchkit/react";

/**
 * The pitch background (outline/stripes/lines) isn't a mark — it's
 * restyled only via `--pitch-surface`/`--pitch-stripe`/`--pitch-lines`.
 * `pitch-surface-*`/`pitch-stripe-*`/`pitch-lines-*` are custom Tailwind
 * utilities (an installable `@utility ... --value(--color-*)` recipe, see
 * the Configuration > Tailwind page) that turn those variables into
 * first-class classes reading any color already in the project's theme.
 */
export function TailwindRecipeBasic() {
  return (
    <Pitch
      type="statsbomb"
      className="pitch-surface-emerald-950 pitch-stripe-emerald-800 pitch-lines-amber-500"
    />
  );
}
```

## Line width

`--pitch-line-width` gets the same recipe shape, but validates a bare/arbitrary number instead
of looking one up in the theme — Tailwind has no rich preset scale for stroke width the way it
does for color:

```css
@utility pitch-line-width-* {
  --pitch-line-width: --value(number, [number]);
}
```

```tsx
import { Pitch } from "@pitchkit/react";

/**
 * `--pitch-line-width` gets the same recipe shape as the color utilities,
 * but validates a bare/arbitrary number instead of looking one up in the
 * theme — Tailwind has no rich preset scale for stroke width to borrow
 * from the way it does for color.
 */
export function TailwindLineWidthBasic() {
  return (
    <Pitch
      type="statsbomb"
      className="pitch-line-width-4 pitch-lines-amber-600 pitch-surface-black pitch-stripe-gray-800"
    />
  );
}
```

`pitch-line-width-4` (bare) and `[--pitch-line-width:4]` (zero-setup arbitrary property) are
equally reasonable here — pick whichever reads better in context.
