Gamification
A kit for progress and rewards. Leaderboard with period tabs, podium, movement arrows and your rank pinned, a badge grid with earned, in-progress and locked states and rarity, achievement card, XP and level progress, streak counter with calendar, reward cards and an unlock toast that respects reduced motion.
Code
import { BadgeGrid, Leaderboard, XpProgress } from "@fadymondy/nasaq/web";export const Page = () => ( <> <XpProgress totalXp={1380} /> <BadgeGrid achievements={[{ id: "a", title: "First step", rarity: "common", earnedAt: "2026-09-01" }]} /> <Leaderboard entries={[{ id: "u1", name: "Layla", score: 4820, previousRank: 2 }]} youId="u1" unit="XP" /> </>);Gamification · beta
Live examples and controls: Gamification in the lab.
Install
npx shadcn@latest add https://docs.nasaqui.com/r/gamification.jsonGeneric parts for showing progress and rewards. Nothing product specific is baked in: you provide the achievements, scores and days, and the kit lays them out, ranks them and works out levels and streaks.
When to use
- Achievements, levels, streaks, rankings and rewards in a learning, fitness, community or team app.
When not to use
- Plain numeric progress: use
Progress. - Tables of ranked data with sorting and filters: use
DataTable.
Import
import { Leaderboard } from "@fadymondy/nasaq/web";
// inside this monorepo: "@nasaq/web"Quick start
import { BadgeGrid, Leaderboard, XpProgress } from "@fadymondy/nasaq/web";
export const Page = () => (
<>
<XpProgress totalXp={1380} />
<BadgeGrid achievements={[{ id: "a", title: "First step", rarity: "common", earnedAt: "2026-09-01" }]} />
<Leaderboard entries={[{ id: "u1", name: "Layla", score: 4820, previousRank: 2 }]} youId="u1" unit="XP" />
</>
);Anatomy
Leaderboard data-slot="leaderboard": header with period Tabs, podium (2, 1, 3), ranked list, pinned "you" row
BadgeGrid filter Tabs (All, Earned, In progress, Locked) and a grid of AchievementMedal
AchievementCard medal, rarity, XP, description, progress or earned date
XpProgress level number, XP in level, bar, XP to the next level
StreakCounter flame, days in a row, longest, at-risk note
StreakCalendar / StreakCard month grid with active days; card combines both
RewardCard art, rarity, cost, Claim with progress and error states
AchievementUnlockToast status region with a medal pop and burstAPI
| Component | Main props |
|---|---|
Leaderboard | entries ({ id, name, score, previousRank?, avatar?, subtitle? }), youId, periods, period, onPeriodChange, unit, limit (10), podium (true), loading, title. |
BadgeGrid | achievements, filters (true), filter, onFilterChange, onSelect(id), selectedId. |
AchievementCard | achievement, actions. |
Achievement | { id, title, description?, icon?, rarity?, progress?, goal?, earnedAt?, xp?, secret? }. |
XpProgress | totalXp, curve ({ base, growth }). |
StreakCounter | current, longest, atRisk. |
StreakCalendar / StreakCard | activeDays, month, onMonthChange, today, weekStart. |
RewardCard | title, description, art, rarity, cost, costUnit, balance, status, lockedReason, onClaim (async, may resolve { error }). |
AchievementUnlockToast | achievement, open, onClose, onView, duration (6000), floating. |
Rarity is common, uncommon, rare, epic or legendary. Tied scores share a rank (1, 2, 2, 4).
Pure helpers, tested: rankEntries, movement, levelProgress, streakStats, monthGrid, achievementStatus.
Examples
Async claim with an error
import { RewardCard } from "@fadymondy/nasaq/web";
export const Reward = () => (
<RewardCard title="Golden frame" cost={800} balance={1200} rarity="rare" onClaim={async () => ({ error: "Out of stock." })} />
);Accessibility
- Rarity, status and movement are always words as well as colour or icon (screen reader text on arrows).
- Your row is marked
aria-current. The pinned row is a labelled list. - The unlock toast is a polite
role="status"that stays mounted, pauses its timer on hover and focus, and can be dismissed. - Under
prefers-reduced-motionthe toast is a still card: no pop and no burst.
RTL & i18n
English and Arabic follow the Nasaq locale, override with labels. Numbers use Num, the calendar starts on
Saturday in Arabic, the ring fill mirrors.
Styling & tokens
Tokens only. Epic uses the violet tag hue and legendary the accent. No custom CSS: the burst uses the Web Animations API.
Do / Don't
- Do let people see how to earn a locked badge.
- Don't make rank the only way to feel progress: show personal progress too.
- Don't use the celebration for routine events.