Standings
betaA league table or a tournament pool, on DataTable: the place, the team and its kit, the counts, points difference, points, and form, with zones.
Preview
league, zones, you, form
| Place | Team | Played | Won | Drawn | Lost | Points difference | Bonus points | Points | Form |
|---|---|---|---|---|---|---|---|---|---|
| 1, Promotion | Lions | 10 | 8 | 0 | 2 | +96 | 6 | 38 | |
| 2, Promotion | You, Rovers | 10 | 7 | 1 | 2 | +64 | 5 | 35 | |
| 3 | Wolves | 10 | 5 | 1 | 4 | +12 | 4 | 26 | |
| 4 | Hawks | 10 | 4 | 0 | 6 | 0 | 3 | 19 | |
| 5 | Tigers United | 10 | 3 | 0 | 7 | −51 | 4 | 16 | |
| 6, Relegation | Falcons | 10 | 1 | 0 | 9 | −121 | 2 | 6 |
pool, form false, one zone
| Place | Team | Played | Won | Drawn | Lost | Points for | Points against | Points difference | Points |
|---|---|---|---|---|---|---|---|---|---|
| 1, Cup quarter-finals | You, Rovers | 3 | 3 | 0 | 0 | 74 | 31 | +43 | 9 |
| 2, Cup quarter-finals | Lions | 3 | 2 | 0 | 1 | 58 | 40 | +18 | 6 |
| 3 | Hawks | 3 | 1 | 0 | 2 | 36 | 55 | −19 | 3 |
| 4 | Falcons | 3 | 0 | 0 | 3 | 22 | 64 | −42 | 0 |
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/standings
Use it for
- A league table or a tournament pool. Put it in a Card with className="px-0", and name it with the card's title through aria-labelledby.
- The app works out the numbers and the order. The table only shows them.
- layout league shows P W D L PD BP Pts. pool shows P W D L PF PA PD Pts.
- zones mark places that mean something: promotion from the top in success, relegation from the bottom in danger. Give each a note, so its peek says what it means.
- you marks your team: a blue bar, its own wash, and its name in the strong weight.
- In a narrow card the counts give way, PF and PA first, then form, then D and BP, and under 20rem W and L, so the team keeps its room.
Avoid it for
- A row inserted between zones. A zone's edge is its 2px rule.
- Real club colors in a demo. Pass theme tokens, like var(--chart-1).
- Ranking people. Use Leaderboard.
Props
- rows
Required, in table order. Each: { id, team: { name, colors?: KitColors, pattern? }, position?, played, won, drawn, lost, pointsFor?, pointsAgainst?, diff?, bonus?, points, form?: FormResult[] }.
StandingsRow[]
- layout
Which counts show.
"league" | "pool" · default "league"
- you
Your team's id.
string
- zones
Places counted from the top or the bottom. tone defaults to success from the top and danger from the bottom.
{ label: string; top?: number; bottom?: number; tone?: "success" | "danger" | "warning" | "brand"; note?: string }[]
- form
The form column, when any row has form.
boolean · default true
- caption
Names the table, under it.
ReactNode
- aria-label
Names the table when it has no caption.
string
- aria-labelledby
Names the table by another element, like the Card's title.
string
- loading
Holds the space with placeholder rows.
boolean · default false
- className
Extra classes on the wrapper. Later classes win.
string
| Prop | Type | Default | What it does |
|---|---|---|---|
| rows | StandingsRow[] | - | Required, in table order. Each: { id, team: { name, colors?: KitColors, pattern? }, position?, played, won, drawn, lost, pointsFor?, pointsAgainst?, diff?, bonus?, points, form?: FormResult[] }. |
| layout | "league" | "pool" | "league" | Which counts show. |
| you | string | - | Your team's id. |
| zones | { label: string; top?: number; bottom?: number; tone?: "success" | "danger" | "warning" | "brand"; note?: string }[] | - | Places counted from the top or the bottom. tone defaults to success from the top and danger from the bottom. |
| form | boolean | true | The form column, when any row has form. |
| caption | ReactNode | - | Names the table, under it. |
| aria-label | string | - | Names the table when it has no caption. |
| aria-labelledby | string | - | Names the table by another element, like the Card's title. |
| loading | boolean | false | Holds the space with placeholder rows. |
| className | string | - | Extra classes on the wrapper. Later classes win. |
Accessibility
- A real <table>. Each team's name is the row header, so every count is read with it.
- Short headers, like P, are read as their full words, like Played, and show them on a mouse rest.
- A zone's place number is read with its label, like "1, Promotion". Its peek says what the zone means.
- Points difference uses a true minus sign, which screen readers read as minus.
- A zone's edges and your team's bar are borders, so they stay in forced colors.
Examples
League with promotion and relegation zones
<Standings
aria-label="League"
you="rov"
zones={[
{ label: "Promotion", top: 1, note: "The top team goes up." },
{ label: "Relegation", bottom: 1, note: "The bottom team goes down." },
]}
rows={[
{ id: "lio", team: { name: "Lions", colors: ["var(--chart-1)", "var(--background)"] }, played: 10, won: 8, drawn: 0, lost: 2, diff: 96, bonus: 6, points: 38 },
{ id: "rov", team: { name: "Rovers", colors: ["var(--foreground)", "var(--chart-3)"], pattern: "hoops" }, played: 10, won: 6, drawn: 1, lost: 3, diff: 41, bonus: 5, points: 31 },
{ id: "fal", team: { name: "Falcons" }, played: 10, won: 1, drawn: 1, lost: 8, diff: -120, bonus: 2, points: 8 },
]}
/>