Display
Leaderboard
A ranked list where each row is a react-aria ProgressBar whose brand-teal fill encodes its value relative to the leader, styled with the quebi design system.
leaderboardrankingchartprogresslist
Default
Each row's brand-teal fill is scaled to the leader's score.
Top players
- 1.Nova4,820
- 2.Echo3,910
- 3.Vex3,240
- 4.Lyra2,105
- 5.Orion1,180
Actionable rows
Pass onAction to make rows clickable — the fill brightens on hover.
Leaderboard
- 1.Nova4,820
- 2.Echo3,910
- 3.Vex3,240
Source
Copy this into your project. Resolve its dependencies from the registryDependencies in the component's API entry.
"use client"
import { Label, type LabelProps, ProgressBar, type ProgressBarProps } from "react-aria-components"
import { cn } from "@/lib/utils"
/**
* Leaderboard — quebi design system
*
* A compact ranked list where each row is a react-aria ProgressBar whose
* fill encodes its value relative to the leader. The fill is a translucent
* brand-teal track that brightens on hover for actionable rows. Compose from
* Leaderboard, LeaderboardHeader, LeaderboardTitle, LeaderboardAction,
* LeaderboardContent, LeaderboardItem, LeaderboardStart, and LeaderboardEnd.
*/
export function Leaderboard({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="leaderboard"
className={cn("flex flex-col gap-y-4", className)}
{...props}
/>
)
}
export function LeaderboardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="leaderboard-header"
className={cn(
"grid auto-rows-min grid-rows-[auto_auto] items-start gap-1 has-data-[slot=leaderboard-action]:grid-cols-[1fr_auto]",
className,
)}
{...props}
/>
)
}
export function LeaderboardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="leaderboard-title"
className={cn("text-balance font-semibold text-base/6 text-white", className)}
{...props}
/>
)
}
export function LeaderboardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="leaderboard-action"
className={cn("col-start-2 row-span-2 row-start-1 self-start justify-self-end", className)}
{...props}
/>
)
}
export function LeaderboardContent({ className, ...props }: React.ComponentProps<"ul">) {
return (
<ul
data-slot="leaderboard-content"
className={cn("flex max-h-96 list-none flex-col gap-y-1", className)}
{...props}
/>
)
}
interface LeaderboardItemProps extends ProgressBarProps {
onAction?: () => void
}
export function LeaderboardItem({
minValue = 0,
className,
children,
onAction,
...props
}: LeaderboardItemProps) {
return (
<li className="group" data-slot="leaderboard-item">
<ProgressBar
onClick={onAction}
minValue={minValue}
className={cn(
"relative overflow-hidden rounded-quebi-sm px-2 py-1.5 text-sm/6 text-white outline-none",
"transition-colors duration-150",
"focus-visible:ring-2 focus-visible:ring-quebi-brand/50 focus-visible:ring-offset-2 focus-visible:ring-offset-quebi-bg",
onAction ? "cursor-pointer hover:bg-white/[0.02]" : "cursor-default",
"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
{(values) => (
<>
<span className="relative z-[2] flex items-center justify-between font-medium">
{typeof children === "function" ? children(values) : children}
</span>
<span
data-slot="leaderboard-fill"
className={cn(
"absolute inset-y-0 start-0 z-[1] rounded-e-quebi-sm bg-quebi-brand/15 transition-colors duration-150",
onAction ? "group-hover:bg-quebi-brand/25" : "",
)}
style={{ width: `${values.percentage}%` }}
/>
</>
)}
</ProgressBar>
</li>
)
}
export function LeaderboardStart({ className, ...props }: LabelProps) {
return (
<Label
data-slot="leaderboard-start"
className={cn("flex items-center gap-x-2", className)}
{...props}
/>
)
}
export function LeaderboardEnd({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="leaderboard-end"
className={cn("tabular-nums text-quebi-fg-muted", className)}
{...props}
/>
)
}