Leaderboard Table — Free HTML CSS JS Snippet

Leaderboard Table · Tables · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Gold/silver/bronze gradient medal badges with colour-matched box-shadow glow
Top-3 row tint: subtle rgba background on medal rows for visual separation
Progress bars as percentage of top score with per-user gradient colours
Rank change indicators: up (green +N), down (red -N), same (grey –)
Period tab switcher with pill active state (white on grey container)
Score column: font-feature-settings "tnum" tabular numbers for column alignment
Streak counter column with fire emoji and day count
Avatar gradient initials with name and role sub-label in flex column
Responsive: .hide-sm hides streak column on narrow screens

About this UI Snippet

Leaderboard Table — Medal Ranks, Progress Bars, Streak Counter & Period Tab Switcher

Screenshot of the Leaderboard Table snippet rendered live

A leaderboard table ranks users, teams, or entities by a score metric — displaying position, identity, score, relative progress, and rank movement. Leaderboards are used in learning platforms, gamified apps, sales dashboards, fitness trackers, and developer community sites to drive engagement and competition. This snippet provides a complete leaderboard table with gradient medal badges, avatar initials, streak counters, score display, progress bars relative to the top score, rank change indicators, and a period tab switcher.

Medal rank badges

Ranks 1–3 use gradient medal badges: gold (amber → orange), silver (slate → grey), bronze (dark amber). Each uses background: linear-gradient with a matching box-shadow glow in the same colour. Ranks 4+ use a neutral grey background. The medal div is a fixed 28×28px square with border-radius: 8px for a rounded square shape — different from the circular avatar to maintain visual separation.

Progress bars relative to top score

Each row's progress bar width is set as a percentage of the top score (not 100%). Row 1 gets 98% (not 100% to avoid exact full-fill), row 2 gets 91%, and so on. Each bar uses a unique gradient matching the medal or avatar colour — creating colour-coded rows without repeating colours, the same fill technique as the standalone progress bar snippet. The bar transition: width 0.6s ease animates on the period tab switch.

Rank change indicators

The .change span shows +N (green), -N (red), or – (grey) for rank movement since the previous period. These use the same tinted background pattern as status badges elsewhere in the library.

Period tab switcher

Three tabs (Week, Month, All time) switch the data period. The active tab gets a white background on the grey tab container — the standard pill-style tab switcher pattern. In production, the tab click triggers an API fetch for the selected period and re-renders the table.

Score display

Scores use font-feature-settings: "tnum" (tabular numbers) so digits align vertically — a critical detail for numerical columns where values need to be visually compared across rows. Without tabular numbers, proportional digit widths cause misalignment in number columns.

Streak counter

A fire emoji + day count (🔥 14d) communicates daily engagement streaks. This is a lightweight gamification signal that is increasingly standard on learning and fitness platforms.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You do not have to reverse-engineer the bar-fill math by reading the markup alone. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain precisely why setPeriod() resets each bar-fill width to 0% before reapplying the stored width, and why row 1 is hardcoded to 98% rather than 100%. The same assistant can help optimize it, for instance asking whether font-feature-settings "tnum" is enough for numeric alignment across locales, or how to avoid a layout thrash if hundreds of rows animate their bar widths at once. It is also useful for extending the table: ask it to wire setPeriod to a real fetch call with a loading skeleton, add a sortable column click handler, or highlight the current signed-in user's row with a sticky footer. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "leaderboard table" in plain HTML, CSS, and JavaScript with no libraries.

Requirements:
- A table with columns for rank, user (avatar initials plus name and role), a streak count, a numeric score, a progress bar relative to the top score, and a rank-change indicator (up, down, or unchanged).
- The top three rows must get distinct gold, silver, and bronze rounded-square medal badges (not circular avatars) built from CSS linear-gradient backgrounds with a matching colored box-shadow glow, plus a subtle tinted row background to set them apart from the rest of the table.
- Each row's progress bar width must be calculated as a percentage of the top scorer's score, capped at 98% even for the top row so no bar looks artificially maxed out, and each bar's fill color should be distinct per row rather than a single repeated color.
- The score column must use font-feature-settings: "tnum" so digits align vertically in a monospaced-number column regardless of proportional font metrics.
- Three period-switching tabs (e.g. Week, Month, All time) using a pill-style active state (white background on a light gray track). Clicking a tab must not fetch new data in this demo, but must re-trigger the bar-fill animation by resetting each bar's width to 0% and then back to its stored value on a short delay, so the CSS width transition visibly replays.
- A responsive rule that hides the streak column entirely below a defined breakpoint rather than shrinking it illegibly.
- Do not hardcode row count assumptions elsewhere in the JS — the period-switch bar animation logic must work against however many .bar-fill elements currently exist in the DOM.

Step by step

How to Use

  1. 1
    Click the period tabs to see the bar animationClick Week, Month, or All time to trigger the bar width animation (bars drop to 0 then refill). In production, wire each tab to an API fetch that returns the ranked data for that period.
  2. 2
    Update user dataIn the HTML tbody, edit each row: change avatar initials, gradient colours, name, role, streak count, score, bar fill width percentage, and change indicator class (up/down/same) and value.
  3. 3
    Add or remove rowsDuplicate any table row pair in the tbody. Update the data-values and assign .top1/.top2/.top3 class only to the first three rows. Remove those classes from all others — the medal gradient and subtle row tint apply automatically.
  4. 4
    Calculate bar widthsSet each bar width as (score / topScore) * 100 + "%". Round to the nearest integer. The top scorer gets ~98% (not 100%) so the bar does not look artificially maxed out.
  5. 5
    Change the score label and period tabsUpdate the th "Score" header to your metric (Points, Revenue, Commits, Completions). Edit the three period button labels to match your time period options (Day/Week/Month, Sprint/Quarter/Year, etc.).
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component mapping a sorted data array to rows, or "Tailwind" for a React + Tailwind CSS version.

Real-world uses

Common Use Cases

Learning platform and course completion leaderboards
Course completion percentage, quiz scores, and XP points make excellent leaderboard metrics. The streak counter (🔥 14d) is a standard gamification element on learning platforms like Duolingo and Codecademy — pair it with the streak tracker widget to reward daily engagement habit formation.
Sales team performance and quota attainment rankings
Revenue closed, deals won, and pipeline value are natural leaderboard metrics for sales teams. The progress bar relative to the top performer communicates distance to the leader at a glance — top it with stats cards for team totals. Period tabs switch between weekly sprint and monthly quota tracking.
Developer contribution and commit leaderboards on team dashboards
Commits, pull requests, code review comments, and issues closed map directly to the score column. Wire to your GitHub or GitLab API to show real contribution data per team member for the selected period.
Gaming and community platform high score tables
The medal badge and gradient progress bar are visually engaging for game high score tables. The rank change indicator (+2, -1) adds competitive context between play sessions. Show top 10 or top 50 with a "Your rank: #23" sticky footer row for the current user.
Study tabular numbers and rank badge CSS techniques
font-feature-settings: "tnum" enables OpenType tabular number figures — a typographic detail essential for numerical columns. The medal gradient badges show how a single div with linear-gradient background and box-shadow creates a dimensional badge effect.
Fitness challenge and step-count leaderboards
Step counts, calories burned, workout minutes, and active days make excellent fitness leaderboard metrics. The streak counter and progress bar communicate daily habit data at a glance. Period tabs toggle between weekly and monthly challenge views.

Got questions?

Frequently Asked Questions

Divide each user's score by the top score and multiply by 100: const width = Math.round((score / topScore) * 100). Set this as the bar width percentage. Cap the top scorer at 98% (not 100%) so the bar does not appear artificially maxed: const width = Math.min(98, Math.round(score/topScore*100)). In a JavaScript-rendered table, compute this in the data mapping: rows.map((r,i) => ({...r, barWidth: Math.min(98, Math.round(r.score/rows[0].score*100))})).

Call your API on period tab click: fetch("/api/leaderboard?period=week").then(r=>r.json()).then(data => renderTable(data)). In renderTable, generate the table rows from the sorted data array: data.forEach((user, i) => { const rank = i+1; const tr = document.createElement("tr"); tr.innerHTML = ...; tbody.appendChild(tr); }). Clear the tbody before each render to prevent duplicate rows.

Compare each row's user ID to the current user ID: if (row.userId === currentUserId) { tr.classList.add("current-user"); }. In the CSS: .current-user td { background: rgba(99,102,241,0.05); } and .current-user .uname { color: #6366f1; font-weight: 800; }. Optionally add a "(You)" label after the name for unambiguous identification.

Click "JSX" to download. Accept a data prop: an array of {rank, name, role, score, streak, change, barWidth} objects sorted by score descending. Use useMemo to compute barWidth from scores. Manage activePeriod in useState. On period change, fetch new data and update the data state. Animate bars by setting a key on the table body that changes on data update, triggering a re-mount and re-animation.