# Bracket Match

> One knockout match on its own, 124px tall and as wide as its column, with a peek that adds what the tile has no room for: the round, the result in words, and facts like the venue or form.

- Name: `bracket-match`
- Status: beta
- Categories: sport, display
- File: the install writes `bracket-match.tsx` to your `ui` alias, `@/components/ui` by default.
- Other registry items: `@arbokui/hover-card`, `@arbokui/knockout-bracket`, `@arbokui/peek-card`. The install adds them.
- Gallery: https://arbokui.com/components/bracket-match
- Registry item: https://arbokui.com/r/bracket-match.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/bracket-match
   ```

## Use it for

- A single knockout fixture in a feed, a list, or a side panel.
- Pass the same BracketMatchData a KnockoutBracket takes. It shows the date and time before kick-off, and FT once it's finished.
- Pass round, like "Cup semi-final", so its peek says which round it is.
- Pass onClick and selected to make it a match you choose, like a card that opens the match panel.
- Use badge for "Live 9'", "AET", or "Forfeit". It replaces FT. A level score with kicks reads FT (P).

## Avoid it for

- A whole draw. Use KnockoutBracket, which lays the tiles out round by round.
- A fixture outside a knockout, or with a flat team shape. Use MatchCard, which has no peek.
- Putting the peek's facts only in the peek. The tile already shows them, so the peek just repeats them.

## Props

- `match`: `BracketMatchData`. The match: { id, date?, time?, status?, home, away, winner?, badge? }. Each side is { team, score, penalties?, label? }.
- `round`: `string`. The round's name. It titles the peek, like "Cup semi-final". Without it, the teams do.
- `facts`: `PeekFact[]`. What the tile cannot show, for the peek, as \[label, value\] pairs, like \["Venue", "Riverside Park"\] or each side's form. The peek never repeats the tile's teams, score, or date.
- `selected`: `boolean`. Default `false`. Draws it as the chosen match. Only a match with onClick can be chosen.
- `onClick`: `() => void`. Makes the match a button that says whether it is pressed.
- `width`: `number`. A fixed width in px, never under 240. By default it fills its column.
- `effect`: `"rise" | "grow"`. Default `"rise"`. How the peek opens and closes. rise fades in and moves out from the match. grow fades in and grows from the match, and closes the same way. Left out, it follows an effect-\<name> class on \<body>, where popups open, and rises with none.
- `className`: `string`. Extra classes for the root. Later classes win.

## Accessibility

- The tile names both sides, the scores, and the winner in text. The winner's caret and weight only show it.
- With onClick, the tile is a button with aria-pressed, and the peek follows its hover and focus. Enter stays the button's own.
- Without onClick, the tile is the peek's trigger: a button that says it opens a dialog.
- The peek is a dialog named by its title: the round, or the two teams when there is no round.
- A chosen match has a 2px outline that shows in forced colors.

## Examples

### Finished, with the round and the venue in its peek

```tsx
<BracketMatch
  round="Cup semi-final"
  facts={[
    ["Venue", "Riverside Park"],
    ["Rovers' form", "W W D W"],
  ]}
  match={{
    id: "sf1",
    date: "Sat 17 Oct",
    time: "15:20",
    status: "finished",
    home: { team: { name: "Rovers" }, score: 24 },
    away: { team: { name: "Lions" }, score: 17 },
  }}
/>
```

### Chosen, with onClick

```tsx
<BracketMatch match={match} round="Final" selected={selected === match.id} onClick={() => setSelected(match.id)} />
```

### Live, at a fixed width

```tsx
<BracketMatch
  width={280}
  match={{
    id: "f",
    time: "15:00 · Pitch 1",
    badge: "Live 9'",
    home: { team: { name: "Hawks" }, score: 3 },
    away: { team: { name: "Wolves" }, score: 0 },
  }}
/>
```

### Peek grows from the match

```tsx
<BracketMatch
  effect="grow"
  round="Cup final"
  match={{
    id: "f1",
    date: "Sat 24 Oct",
    time: "15:00",
    home: { team: { name: "Rovers" }, score: null },
    away: { team: { name: "Hawks" }, score: null },
  }}
/>
```
