Knockout Wheel
betaThe draw as a circle: the champion at the hub with a trophy and a warm glow, and each round a ring further out. Hover or focus a team to light its run.
Preview
eight sides, played out: the champion's run is lit. Hover, focus, or press a team to light its own
Arrow keys move between teams: up toward the champion, down to the round before, and left and right around a ring. Enter keeps a team's run lit.
Champion: Hawks.
sixteen sides
Arrow keys move between teams: up toward the champion, down to the round before, and left and right around a ring. Enter keeps a team's run lit.
Champion: Rovers.
halfway: the quarter-finals are played, and the hub waits for a champion
Arrow keys move between teams: up toward the champion, down to the round before, and left and right around a ring. Enter keeps a team's run lit.
Champion to be decided.
initialRound={1} starts the outer ring at the quarter-finals, at maxWidth={300}
Arrow keys move between teams: up toward the champion, down to the round before, and left and right around a ring. Enter keeps a team's run lit.
Champion: Rovers.
Install
1. Add the registry to
components.json, once per app. Without it, the shadcn CLI looks up@txbrendanin a public list that txbrendan/ui does not control.npx shadcn@latest registry add "@txbrendan=https://txbrendan.dev/r/{name}.json"2. Add the theme, once per app. It replaces your color tokens with ones checked for contrast.
npx shadcn@latest add @txbrendan/theme3. Add the component.
npx shadcn@latest add @txbrendan/knockout-wheel
Use it for
- A season's draw at a glance, like on a cup's home page or once the final is played.
- Pass the same rounds a KnockoutBracket takes, widest first.
- Pass initialRound to start the outer ring at a later round, so a big draw stays readable.
- Give each team its kit colors. Each node is the team's TeamFlag, cut to a circle.
- Pair it with a KnockoutBracket or a list when people need the dates and scores at a glance. The wheel says them only to screen readers and on focus.
Avoid it for
- Reading the dates, times, or pitches. Use KnockoutBracket, which shows them on each match.
- Draws of more than 32 sides. The outer ring gets too tight to point at. Start at a later round with initialRound.
- A league. Nothing feeds anything, so there is no wheel to draw.
Props
- rounds
Widest round first. rounds[r].matches[k] is fed by matches 2k and 2k+1 of the round before.
BracketRound[]
- initialRound
The index of the outermost round to draw.
number · default 0
- maxWidth
The widest it draws, in px. It fills a narrower container, down to 280px, or maxWidth if less. Under 280px the hub shows the trophy alone.
number · default 544
- aria-label
Names the wheel.
string · default "Knockout wheel"
- className
Extra classes. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| rounds | BracketRound[] | - | Widest round first. rounds[r].matches[k] is fed by matches 2k and 2k+1 of the round before. |
| initialRound | number | 0 | The index of the outermost round to draw. |
| maxWidth | number | 544 | The widest it draws, in px. It fills a narrower container, down to 280px, or maxWidth if less. Under 280px the hub shows the trophy alone. |
| aria-label | string | "Knockout wheel" | Names the wheel. |
| className | string | - | Extra classes. Later classes win. |
Accessibility
- The wheel is a group, with each round's ring a list named by the round, outermost first, then the champion.
- Each team is a button that names the team, the round, and the result, like "Rovers, Semi-finals: beat Lions 24–17". So each match names both teams and who won.
- One Tab stop. Up goes toward the champion, down to the round before, left and right around the ring, and Home and End to a ring's ends. The group's description says so.
- Enter, Space, or a tap keeps a team's run lit, and the button says it is pressed. Press it again, or Esc, to let go.
- The lit run is a thicker line and a ring around each of its nodes, so it shows in forced colors too, not only by its color.
- The rings and the lines are drawn in a hidden SVG. The hub's readout is hidden too, since each button says the same.
- Nodes spring in only from the motion tokens. Under reduced motion they fade, and with motion off they just show.
Examples
A whole draw, played out
// rounds is the same BracketRound[] a KnockoutBracket takes.
<KnockoutWheel rounds={rounds} />From the quarter-finals, in a narrow column
<KnockoutWheel rounds={rounds} initialRound={1} maxWidth={320} aria-label="Cup draw from the quarter-finals" />