A fresh task each day
A card presenting today's challenge with a single clear action. Daily goals bring people back — keep it small and achievable.
Today's Challenge
Build a responsive card with Flexbox.
HTML
<div class="challenge"> <i class="ph ph-target"></i> <h4>Today's Challenge</h4> <p>Build a responsive card with Flexbox.</p> <button>Start — +50 XP</button> </div>
CSS
.challenge {
border: 1px solid #e5e7eb; border-radius: 14px;
padding: 18px; text-align: center; max-width: 340px;
}
.challenge button { background: #38bdf8; color: #fff; border: none;
border-radius: 8px; padding: 10px 18px; }
Keep the chain going
A row of day markers showing completed days, today, and upcoming — plus a streak count. Visible progress is a strong motivator.
HTML
<div class="streak"> <span class="day done">M</span> <span class="day done">T</span> <span class="day done">W</span> <span class="day today">T</span> <span class="day">F</span> <span class="day">S</span> <span class="day">S</span> <span class="flame"><i class="ph ph-fire-fill"></i> 3-day streak</span> </div>
CSS
.day {
width: 34px; height: 34px; border-radius: 50%;
display: grid; place-items: center;
background: #e5e7eb; color: #6b7280;
}
.day.done { background: #22c55e; color: #fff; } /* completed */
.day.today { background: #38bdf8; color: #fff; } /* current */
Reward the finish line
A certificate marks a milestone — a course finished, a level cleared. A double border, a seal, and a serif headline give it that “official” feel.
Web Design Foundations
has successfully completed the course — May 2026
HTML
<div class="certificate"> <div class="seal"><i class="ph ph-seal-check"></i></div> <p class="kicker">Certificate of Completion</p> <h3>Web Design Foundations</h3> <p class="name">Jordan Parker</p> <p>has successfully completed the course — May 2026</p> </div>
CSS
.certificate {
border: 3px double #c9a227; /* classic double border */
border-radius: 12px;
padding: 26px; text-align: center;
background: #fffdf5;
}
.certificate h3 { font-family: Georgia, serif; }
.certificate .name { border-bottom: 2px solid #c9a227; }
Earnable badges in tiers
Badges reward specific accomplishments. Use tiers (bronze / silver / gold) and show locked ones greyed-out so people see what's next to earn.
HTML
<div class="gm-badges"> <div class="gm-badge gold"><div class="gm-medal"><div class="disc"><i class="ph ph-trophy-fill"></i></div></div><div class="nm">First Site</div><div class="lv">Gold</div></div> <div class="gm-badge silver"><div class="gm-medal"><div class="disc"><i class="ph ph-paint-brush-fill"></i></div></div><div class="nm">Stylist</div><div class="lv">Silver</div></div> <div class="gm-badge bronze"><div class="gm-medal"><div class="disc"><i class="ph ph-code-fill"></i></div></div><div class="nm">First Code</div><div class="lv">Bronze</div></div> <div class="gm-badge locked"><div class="gm-medal"><div class="disc"><i class="ph ph-lock-fill"></i></div></div><div class="nm">Deploy Pro</div><div class="lv">Locked</div></div> </div> <!-- Load Phosphor icons in <head>: --> <!-- <script src="https://unpkg.com/@phosphor-icons/web"></script> -->
CSS — starburst “merit seal” shape with ribbon
.gm-badges { display: flex; gap: 24px; flex-wrap: wrap; }
.gm-badge { text-align: center; width: 96px; }
.gm-medal { position: relative; width: 78px; height: 78px; margin: 0 auto 8px; }
/* the ribbon tails behind the seal */
.gm-medal::before, .gm-medal::after { content: ""; position: absolute; bottom: -16px;
width: 16px; height: 30px; background: var(--ribbon, #94a3b8); z-index: 0; }
.gm-medal::before { left: 24px; transform: rotate(8deg);
clip-path: polygon(0 0,100% 0,100% 100%,50% 78%,0 100%); }
.gm-medal::after { right: 24px; transform: rotate(-8deg);
clip-path: polygon(0 0,100% 0,100% 100%,50% 78%,0 100%); }
.gm-badge .disc { position: relative; z-index: 1; width: 78px; height: 78px;
display: grid; place-items: center; font-size: 1.8rem; color: #fff;
transition: transform .25s;
clip-path: polygon(50% 0,61% 12%,77% 7%,79% 24%,95% 27%,87% 42%,100% 50%,87% 58%,
95% 73%,79% 76%,77% 93%,61% 88%,50% 100%,39% 88%,23% 93%,21% 76%,5% 73%,
13% 58%,0 50%,13% 42%,5% 27%,21% 24%,23% 7%,39% 12%); }
/* inner ring for the classic seal look */
.gm-badge .disc::after { content: ""; position: absolute; inset: 16px;
border-radius: 50%; border: 2px solid rgba(255,255,255,.5); }
.gm-badge .disc > i { position: relative; z-index: 1;
filter: drop-shadow(0 1px 1px rgba(0,0,0,.4)); }
.gm-badge:hover .disc { transform: rotate(10deg) scale(1.06); }
.gm-badge.gold .disc { background: radial-gradient(circle at 36% 28%, #fff7d6, #f5b942 52%, #b8780a); }
.gm-badge.gold { --ribbon: #d97706; }
.gm-badge.silver .disc { background: radial-gradient(circle at 36% 28%, #ffffff, #cfd4da 52%, #8a9099); }
.gm-badge.silver { --ribbon: #94a3b8; }
.gm-badge.bronze .disc { background: radial-gradient(circle at 36% 28%, #ffd9a8, #d98c3f 52%, #8a4f16); }
.gm-badge.bronze { --ribbon: #b45309; }
.gm-badge.locked .disc { background: #e2e5ea; color: #b0b6bf; }
.gm-badge.locked { --ribbon: #cbd5e1; }
.gm-badge.locked .disc::after { border-color: rgba(255,255,255,.7); }
.gm-badge .nm { font-size: .76rem; font-weight: 700; color: #374151; }
.gm-badge .lv { font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }
.gm-badge.gold .lv { color: #b45309; }
.gm-badge.silver .lv { color: #6b7280; }
.gm-badge.bronze .lv { color: #92400e; }
.gm-badge.locked .lv { color: #9ca3af; }
Pop-up when something's unlocked
An achievements panel tracks what's earned, what's in progress, and what's still locked — each row shows an icon, description, and status. The pop-up toast is the celebration moment when one unlocks; click “Complete a lesson” to see it fire.
HTML — an achievement row, the button, and the toast
<div class="achievement unlocked"> <div class="icon"><i class="ph ph-medal-fill"></i></div> <div><p class="title">First Steps</p><p>Completed your first lesson</p></div> <span class="status">Unlocked</span> </div> <div class="achievement progress"> … <div class="bar"><i style="width:66%"></i></div> <!-- in-progress --> </div> <button id="unlockBtn">Complete a lesson</button> <!-- the celebration toast, hidden until something unlocks --> <div class="toast" id="toast" style="display:none"> <div class="icon"><i class="ph ph-medal-fill"></i></div> <div><p class="title">Achievement Unlocked!</p><p>Finished your first lesson — +100 XP</p></div> </div>
JavaScript — fire the toast on unlock
// Grab the button and the toast by the ids in the HTML above.
const btn = document.getElementById('unlockBtn');
const toast = document.getElementById('toast');
let timer;
btn.addEventListener('click', () => {
toast.style.display = 'flex'; // celebrate
clearTimeout(timer); // reset any pending hide
timer = setTimeout(() => {
toast.style.display = 'none'; // auto-hide after 4s
}, 4000);
});
CSS — the locked / in-progress / unlocked states + the toast
.achievement { display: flex; align-items: center; gap: 14px; background: #fff;
border: 1px solid #e5e7eb; border-radius: 12px; padding: 12px 14px; }
.achievement .icon { width: 44px; height: 44px; border-radius: 10px; display: grid; place-items: center; }
.achievement.unlocked .icon { background: #dcfce7; color: #16a34a; } /* earned */
.achievement.progress .icon { background: #e0f2fe; color: #0284c7; } /* in progress */
.achievement.locked .icon { background: #f1f5f9; color: #cbd5e1; } /* not yet */
.achievement .status { font-size: .7rem; font-weight: 700; padding: 4px 9px; border-radius: 999px; }
.bar { background: #e5e7eb; border-radius: 999px; height: 6px; overflow: hidden; }
.bar i { display: block; height: 100%; background: #0284c7; } /* in-progress fill */
#unlockBtn { background: #38bdf8; color: #fff; border: none; border-radius: 8px;
padding: 10px 18px; font-weight: 700; cursor: pointer; margin-top: 14px; }
/* the celebration toast */
.toast { display: flex; align-items: center; gap: 14px; margin-top: 14px; max-width: 360px;
background: #1f2433; color: #fff; border-radius: 12px; padding: 14px 18px; border-left: 4px solid #f59e0b; }
.toast .icon { width: 42px; height: 42px; border-radius: 50%; background: #f59e0b;
display: grid; place-items: center; font-size: 1.3rem; color: #1a1205; flex: 0 0 auto; }
.toast .title { font-weight: 700; font-size: .92rem; }
Points that accumulate
Experience points give every action a visible reward. Click to earn XP and watch the bar toward the next level fill.
HTML
<div class="xp">
<div class="top">
<span class="lvl">Level 3</span>
<span class="pts"><b id="xpNow">400</b> / 1000 XP</span>
</div>
<div class="bar"><div class="fill" id="xpFill" style="width:40%"></div></div>
<button id="xpBtn">+50 XP</button>
</div>
JavaScript
// Grab the button, the fill bar, and the points label by their ids.
const btn = document.getElementById('xpBtn');
const fill = document.getElementById('xpFill');
const label = document.getElementById('xpNow');
let xp = 400;
const max = 1000;
btn.addEventListener('click', () => {
xp = Math.min(xp + 50, max); // earn 50, capped at max
fill.style.width = (xp / max * 100) + '%'; // grow the bar
label.textContent = xp; // update the number
});
CSS
.xp .top { display: flex; justify-content: space-between; align-items: baseline; }
.lvl { font-weight: 800; }
.bar { background: #e5e7eb; border-radius: 999px; height: 14px; overflow: hidden; }
.fill { height: 100%; width: 40%; background: linear-gradient(90deg,#38bdf8,#22c55e); transition: width .4s; }
.xp button { background: #38bdf8; color: #fff; border: none; border-radius: 8px; padding: 9px 16px; font-weight: 700; cursor: pointer; }
Show how far along they are
A labelled bar that fills toward a goal — for course completion, profile setup, or onboarding. The fill width is the only thing that changes.
HTML & CSS
<div class="top"><span class="lvl">Course progress</span><span class="pts">70% complete</span></div>
<div class="bar"><div class="fill" style="width:70%"></div></div>
.top { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 6px; }
.lvl { font-weight: 800; color: #111; }
.pts { font-size: .82rem; color: #6b7280; }
.bar { background: #e5e7eb; border-radius: 999px; height: 14px; overflow: hidden; }
.fill { height: 100%; background: linear-gradient(90deg,#38bdf8,#22c55e); transition: width .4s; }
<progress value="70" max="100"></progress> element — screen readers announce it automatically.Stages that unlock in order
Levels group progress into named stages. Completed levels lock in, the current one is highlighted, and future ones stay dim.
HTML
<div class="levels"> <div class="level done">1</div> <div class="level done">2</div> <div class="level cur">3</div> <div class="level">4</div> <div class="level">5</div> </div>
CSS
.level { width: 46px; height: 46px; border-radius: 12px; display: grid; place-items: center;
background: #e5e7eb; color: #9ca3af; font-weight: 800; }
.level.done { background: #22c55e; color: #fff; } /* cleared */
.level.cur { background: #38bdf8; color: #fff; box-shadow: 0 0 0 3px rgba(56,189,248,.3); }
Answer to earn points
A timed-feel quiz where a correct answer awards XP. Pick an answer.
HTML
<div class="quiz" id="quiz"> <p class="q">Which unit scales with the root font size?</p> <button class="opt" data-correct="false">px</button> <button class="opt" data-correct="true">rem</button> <button class="opt" data-correct="false">vh</button> <div class="res" hidden></div> </div>
JavaScript
// Grab the quiz container and its result line.
const quiz = document.getElementById('quiz');
const result = quiz.querySelector('.res');
// Wire up every option button.
quiz.querySelectorAll('.opt').forEach(opt => {
opt.addEventListener('click', () => {
if (quiz.dataset.done) return; // only answer once
const right = opt.dataset.correct === 'true';
opt.classList.add(right ? 'correct' : 'wrong');
// If they missed, highlight the real answer too.
if (!right) quiz.querySelector('.opt[data-correct="true"]').classList.add('correct');
quiz.dataset.done = '1';
result.hidden = false;
result.textContent = right
? '+25 XP — correct!'
: 'No points — the answer is highlighted.';
});
});
CSS
.opt { display: block; width: 100%; text-align: left; background: #fff;
border: 1px solid #cbd5e1; border-radius: 8px; padding: 10px 14px; margin: 6px 0; cursor: pointer; }
.opt:hover { border-color: #38bdf8; }
.opt.correct { background: #dcfce7; border-color: #22c55e; color: #166534; } /* right answer */
.opt.wrong { background: #fee2e2; border-color: #ef4444; color: #991b1b; } /* wrong answer */
.res { margin-top: 8px; font-weight: 700; }
Rank players against each other
A ranked list with position, avatar, name, and score. Highlight the current user's row so they can find themselves instantly.
HTML
<div class="gm-lb"> <div class="row"><div class="rk">1</div><div class="av">AR</div><div class="nm">Ava R.</div><div class="sc">2,480</div></div> <div class="row"><div class="rk">2</div><div class="av">JL</div><div class="nm">Jordan L.</div><div class="sc">2,310</div></div> <div class="row me"><div class="rk">3</div><div class="av">YOU</div><div class="nm">You</div><div class="sc">2,090</div></div> <div class="row"><div class="rk">4</div><div class="av">MK</div><div class="nm">Mia K.</div><div class="sc">1,870</div></div> </div>
CSS — highlight the user, gold for first place
.gm-lb { max-width: 420px; background: #fff; border: 1px solid #e5e7eb;
border-radius: 12px; overflow: hidden; }
.gm-lb .row { display: flex; align-items: center; gap: 12px; padding: 11px 16px;
border-bottom: 1px solid #f1f5f9; }
.gm-lb .row:last-child { border-bottom: none; }
.gm-lb .row.me { background: #eff6ff; } /* "you" stand out */
.gm-lb .rk { width: 26px; font-weight: 800; color: #9ca3af; text-align: center; }
.gm-lb .row:nth-child(1) .rk { color: #d97706; } /* gold for first place */
.gm-lb .av { width: 32px; height: 32px; border-radius: 50%; background: #38bdf8;
color: #fff; display: grid; place-items: center; font-weight: 700; font-size: .8rem; }
.gm-lb .nm { flex: 1; color: #374151; font-weight: 600; font-size: .9rem; }
.gm-lb .sc { font-weight: 800; color: #111; }
Celebrate the payoff
A full-moment reward screen after a big milestone — a burst, the prize, and a single button to continue. Keep it brief and joyful.
Level Complete!
You finished the Flexbox module.
HTML
<div class="reward">
<div class="burst"><i class="ph ph-confetti"></i></div>
<h3>Level Complete!</h3>
<p>You finished the Flexbox module.</p>
<div class="loot">
<span>+200 XP</span>
<span><i class="ph ph-medal-fill"></i> Stylist Badge</span>
</div>
<button>Continue</button>
</div>
CSS
.reward {
text-align: center; border-radius: 16px; padding: 28px 22px;
background: radial-gradient(circle at top, #fef3c7, #fff); /* warm glow */
border: 1px solid #fcd34d;
}
.loot span { background: #fde68a; color: #92400e; border-radius: 8px; padding: 6px 12px; }
Keep building
See Gamified Design and Microinteractions — or browse the full Component Library.