Leaderboard Table
Ranked list with ink header, colored podium squares for the top 3, proportional background bars behind each row, rank-change chips (▲/▼) and big tabular values. Sorts by value automatically.
brutalist/ui/leaderboard-tableSource
import { cn } from "@/lib/utils";
export interface LeaderboardEntry {
name: string;
detail?: string;
value: number;
/** Position change since last period (+ up, − down). */
change?: number;
}
export interface LeaderboardTableProps {
title: string;
valueLabel?: string;
entries: LeaderboardEntry[];
format?: (value: number) => string;
className?: string;
}
const PODIUM = ["bg-da-primary text-da-primary-fg", "bg-da-accent text-da-accent-fg", "bg-da-secondary text-da-secondary-fg"];
export function LeaderboardTable({ title, valueLabel = "Score", entries, format = (v) => v.toLocaleString("en-US"), className }: LeaderboardTableProps) {
const sorted = [...entries].sort((a, b) => b.value - a.value);
const max = sorted[0]?.value ?? 1;
return (
<div className={cn("da-stroke bg-da-surface text-da-surface-fg shadow-da-md", className)}>
<div className="da-stroke-b flex items-center justify-between bg-da-fg px-4 py-3 text-da-bg">
<h3 className="font-da-display text-lg tracking-da-display uppercase">{title}</h3>
<span className="font-da-mono text-[11px] font-bold tracking-da-label uppercase">{valueLabel}</span>
</div>
<ol>
{sorted.map((e, i) => (
<li key={e.name} className={cn("relative flex items-center gap-4 px-4 py-3", i > 0 && "da-stroke-t")}>
<span aria-hidden className="absolute inset-y-0 left-0 bg-da-muted" style={{ width: `${(e.value / max) * 100}%` }} />
<span className={cn("da-stroke relative grid size-9 shrink-0 place-items-center font-da-display", PODIUM[i] ?? "bg-da-surface")}>
{i + 1}
<span className="sr-only">. place</span>
</span>
<span className="relative min-w-0 flex-1">
<span className="block truncate font-bold">{e.name}</span>
{e.detail && <span className="block truncate font-da-mono text-xs text-da-muted-fg">{e.detail}</span>}
</span>
{e.change !== undefined && e.change !== 0 && (
<span className={cn("da-stroke relative px-1 font-da-mono text-[11px] font-bold", e.change > 0 ? "bg-da-success text-da-success-fg" : "bg-da-danger text-da-danger-fg")}>
{e.change > 0 ? `▲${e.change}` : `▼${Math.abs(e.change)}`}
<span className="sr-only">{e.change > 0 ? " places up" : " places down"}</span>
</span>
)}
<span className="relative font-da-display text-xl tabular-nums">{format(e.value)}</span>
</li>
))}
</ol>
</div>
);
}
export default LeaderboardTable;
modules/brutalist/ui/leaderboard-table/index.tsx
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | string | — | Header title (h3). |
| valueLabel | string | "Score" | Label of the value column. |
| entries* | LeaderboardEntry[] | — | { name, detail?, value, change? }[]. |
| format | (value: number) => string | — | Value formatter (default en-US thousands). |
| className | string | — | Classes on the wrapper. |
Other table variants in Brutalist
Data Table
Typed generic table with a yellow caption bar + toolbar slot, ruled rows, sortable columns (asc → desc → none), custom cell renderers, mobile column hiding, empty state and optional row click.
Expandable Table
Table with expandable rows: a ruled chevron square per row reveals a detail row underneath; the open row floods yellow. Single or multiple open rows.
Simple Table
Server-safe static table: uppercase caption, ink header row with column rules, zebra rows, right-aligned mono numeric columns and an optional yellow totals footer.
Table in other art directions
Action Items Table
Action items table with Open / Done / All filter pills and counts, gradient checkboxes that strike tasks through, owner, due chip (overdue in red) and source meeting; empty state.
Data Table
Frosted table panel with title, description, pill search field and actions slot; rows are separated translucent rounded strips that brighten on hover. Typed generic columns, sorting, mobile hiding, empty state.
Meetings Table
Recent meetings glass table: title with gradient video icon and platform, date, mono duration, participant avatar stack and a recap status pill (ready, processing, private).
Usage Table
Team usage glass table: member, inline gradient bar of recorded hours relative to the top member, meetings count and hours saved, with a tinted totals row.
Data Table
Typed generic table with hairline rows, sortable headers, optional row selection (select-all with indeterminate state) and a floating bulk-action bar. Custom cells, mobile column hiding, empty state.
Grouped Table
Services table grouped by team: collapsible tinted group rows with service count and alert subtotal, then rows with status dot, mono service name, owner, alerts and MTTA.
Paginated Table
Audit-log table with client-side pagination: page-size select, “1–10 of 47” range, numbered pages with ellipsis and prev/next; mono timestamps and IPs, horizontal scroll on mobile.
Uptime Table
Status-page uptime table: one row per service with 30 daily bars colored by threshold (≥ 99.99 %, ≥ 99 %, below), a current-status dot, the 30-day average and a legend.
Changelog Table
Release log laid out as a table: mono version + ISO date column, a square type tag (New in signal color, Improved outlined, Fixed gray) and dash-listed notes; hairline rows.
Data Table
Analytics table in pure typography: mono headers that sort (↑/↓ glyph, aria-sort), hairline rows, tabular numbers, inline hairline bars for share of views, and an ink total row.
Projects Table
Studio project index (the classic portfolio list): large titles with client, discipline and year in mono columns; each row is one link that inverts to ink on hover with an ↗.
Sites Table
Sites dashboard table: name + domain link, dot status in mono, template, updated time and a “…” Radix dropdown (open, duplicate, archive); hairline rows.
Approvals Table
Bill approval queue: vendor + requester, category, mono amount and inline Approve / Reject buttons that turn the row into a decided state (with Undo); pending count in the header.
Data Table
Customer data table: sortable headers (aria-sort), row checkboxes with select-all (indeterminate), bulk-action bar when rows are selected, mono amounts and pagination.
Invoice Table
Receivables table with status pills, days-late aging text, mono amounts, a totals footer and a per-row Radix dropdown (view, send reminder, duplicate, void).
Ledger Table
General-ledger table grouped by date: journal ref and memo, GL account, debit/credit columns in mono, credits indented, and balanced totals row with a “Balanced” check.
Actions Table
Reduction plan table: round checkboxes mark actions done (row softens), owner and due date, mono savings, and a progress header showing tonnes secured toward the target.
Emissions Table
Emissions by category: rounded cream table with scope leaf chips, inline sage share bars behind the tonnes, mono figures, colored year-over-year change and a total row.
Monthly Table
Month × scope heat table: each cell tinted by intensity along a sand→sage→forest scale with mono values; row totals and a legend; readable without color.
Supplier Table
Supplier engagement table: filter pills by status, arch avatars, share of footprint, status pill and 3-dot data quality meter, with a per-row “Nudge” pill that turns into “Sent”.
Activity Table
Activity log table: kind icon in a pastel circle, emoji avatar and sentence (“Ana completed …”), board chip and relative time; rows highlight softly on hover.
Simple Table
Plain friendly data table in a rounded white card: soft header well, roomy rows, gentle zebra and right-aligned numbers; generic columns and rows props.
Task Table
“My tasks” table where each row is a soft rounded strip: round done checkbox, title (struck when done), pastel board chip, owner initial avatar and due chip (red when overdue).
Workload Table
Team workload heat table: a row per person (emoji + name), one rounded cell per weekday tinted mint → butter → peach by number of tasks, with a legend.