Leaderboard
Entries ranked by a value, with the top three on a podium.
Use it for any list where the order is the point: a fitness challenge, a sales team's month, a game's weekly XP, the fastest times on a course. You pass entries with a value, and the component sorts them, numbers the places and draws the top three on a podium above the rest.
The component works out the ranks, so the data does not carry them. Entries on the same value share a place and the next place is skipped (1, 2, 2, 4), which is how a scoreboard is normally read.
It renders every row it is given. It is not virtualised, so for a long board pass limit and let highlight-id keep the viewer's own row in view.
For one number and its trend, use KPI.
Installation
Copy the folder into components/ui, where you can edit it. It uses the Avatar from this library and the cn helper.
components/ui/leaderboard/
├── Leaderboard.vue
├── rank.ts
└── index.tsUsage
<script setup lang="ts">
import { Leaderboard } from "@/components/ui/leaderboard";
</script>
<template>
<Leaderboard
:data="[
{ id: 'maya', name: 'Maya Chen', value: 84210, change: 2 },
{ id: 'omar', name: 'Omar Haddad', value: 79655 },
{ id: 'lena', name: 'Lena Brandt', value: 71038, change: -1 },
{ id: 'sam', name: 'Sam Okafor', value: 66402, change: 1 },
{ id: 'ines', name: 'Inès Moreau', value: 61987, change: -2 },
]"
unit="steps"
/>
</template>Podium bars to scale
The default podium. Each bar is drawn against the leader's value, so a close race looks close and a runaway looks like one. A floor keeps third place visible as a bar when the leader is far ahead. Set podium-scale="rank" to step the heights by place instead.
Cards instead of bars
podium="cards" gives each of the top three a card, with first raised between the other two. Use it when the values are labels people read (revenue, a level) rather than amounts to compare by eye. format-value controls how the value is written, and the screen reader hears the same text.
Keeping the viewer in sight
highlight-id tints that entry's row. When limit cuts it off, the row is pinned under the list with its real place, after a break that shows rows were skipped.
Lowest first, for times
order="asc" ranks the smallest value first, for a race time or a golf score. The podium then always steps by place, because a bar scaled to a winning time would be the shortest. podium="none" puts everyone in the list.
Movement and presses
change is the number of places an entry moved since the last period: positive is up the board, negative is down, 0 shows a dash. Leave it out and no movement column is drawn. A @select listener makes every row and podium place a button, and receives the entry and its rank.
A short list
With three entries and podium="none", everyone sits in the list. subtitle adds a second line under the name.
Nothing to rank
An empty data array shows empty-text in place of the board. It defaults to "No entries yet".
Step challenge
The bar podium, scaled to the values. Switching weeks swaps the data and the bars regrow to the new totals.
Sales team
Cards for the top three, with revenue written out in currency.
Weekly league
Forty players cut to ten. The viewer is 27th, so their row is pinned under the list with its real place.
Fastest laps
Lowest time first, no podium, and a tie for second.
API Reference
Leaderboard
It also takes the attributes of the element it renders, and a class.
Notes
Accessibility
Each podium place and each row is a single accessible element, read as one sentence: "Rank 2, Omar Haddad, 79,655 steps, up 1 place". A tie adds "tied" after the rank. Pass unit so the value is read with what it counts; without it, the reader hears a bare number.
Reduced motion
The podium bars grow when the board first appears, third place first and the leader last, in CSS, so they are there even before the page hydrates. With reduced motion on, they are drawn at full height straight away.
Colour
The podium uses the theme's primary colour at three strengths rather than gold, silver and bronze. Fixed medal colours would clash with most of the themes, and the place is already shown by the number on the bar.
Public exports
- Values: Leaderboard, rankEntries, podiumHeights
- Types: LeaderboardEntry, LeaderboardPodium, LeaderboardRanked