Flip to reveal the answer
A classic study tool: a term on the front, the answer on the back. Click the card to flip, and use the arrows to move through the deck. The flip is a CSS 3D transform.
HTML
<div class="ed-fc">
<div class="card" id="fcCard">
<div class="face front" id="fcFront">What does CSS stand for?</div>
<div class="face back" id="fcBack">Cascading Style Sheets</div>
</div>
<div class="nav">
<button class="btn ghost" id="fcPrev">‹ Prev</button>
<span class="count" id="fcCount">1 / 3</span>
<button class="btn ghost" id="fcNext">Next ›</button>
</div>
</div>
CSS — the 3D flip
.ed-fc { perspective: 1000px; max-width: 320px; }
.ed-fc .card { position: relative; height: 150px; cursor: pointer;
transform-style: preserve-3d; transition: transform .5s; }
.ed-fc .card.flip { transform: rotateY(180deg); }
.ed-fc .face { position: absolute; inset: 0; backface-visibility: hidden;
border-radius: 12px; display: grid; place-items: center;
padding: 16px; text-align: center; font-weight: 700; }
.ed-fc .front { background: #fff; border: 1px solid #e5e7eb; color: #111; font-size: 1.2rem; }
.ed-fc .back { background: #f59e0b; color: #1a1205; transform: rotateY(180deg); font-size: .95rem; }
.ed-fc .nav { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.ed-fc .count { font-size: .82rem; color: #6b7280; }
.btn { background: #f59e0b; color: #1a1205; border: none; border-radius: 8px;
padding: 9px 16px; font-weight: 700; cursor: pointer; font-size: .88rem; }
.btn.ghost { background: #fff; border: 1px solid #cbd5e1; color: #374151; }
JavaScript — flip & step through the deck
// A small deck of [front, back] pairs
const deck = [
['What does CSS stand for?', 'Cascading Style Sheets'],
['What does HTML stand for?', 'HyperText Markup Language'],
['Which tag makes a link?', 'The <a> (anchor) tag']
];
let i = 0;
const card = document.getElementById('fcCard');
const front = document.getElementById('fcFront');
const back = document.getElementById('fcBack');
const count = document.getElementById('fcCount');
function show() { // draw the current card, un-flipped
card.classList.remove('flip');
front.textContent = deck[i][0];
back.textContent = deck[i][1];
count.textContent = (i + 1) + ' / ' + deck.length;
}
card.addEventListener('click', () => card.classList.toggle('flip')); // click to flip
document.getElementById('fcPrev').addEventListener('click', () => { i = (i - 1 + deck.length) % deck.length; show(); });
document.getElementById('fcNext').addEventListener('click', () => { i = (i + 1) % deck.length; show(); });
show();
Multiple choice with instant feedback
Pick an answer and the quiz marks it right or wrong immediately, then tracks a running score. Try answering.
HTML
<div class="ed-quiz" id="quiz"> <div class="q">Which CSS property makes a flex container's items sit in a row?</div> <button class="opt" data-correct="false">display: grid</button> <button class="opt" data-correct="true">flex-direction: row</button> <button class="opt" data-correct="false">float: left</button> <div class="score" id="quizScore" hidden></div> </div>
CSS
.ed-quiz .q { font-weight: 700; color: #111; margin-bottom: 10px; }
.ed-quiz .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; font-size: .9rem; color: #374151; }
.ed-quiz .opt:hover { border-color: #f59e0b; }
.ed-quiz .opt.correct { background: #dcfce7; border-color: #22c55e; color: #166534; }
.ed-quiz .opt.wrong { background: #fee2e2; border-color: #ef4444; color: #991b1b; }
.ed-quiz .score { margin-top: 10px; font-weight: 700; color: #0b3d91; }
JavaScript
const quiz = document.getElementById('quiz');
const score = document.getElementById('quizScore');
quiz.querySelectorAll('.opt').forEach(btn => {
btn.addEventListener('click', () => {
if (quiz.dataset.done) return; // only answer once
const right = btn.dataset.correct === 'true';
btn.classList.add(right ? 'correct' : 'wrong');
if (!right) quiz.querySelector('.opt[data-correct="true"]').classList.add('correct');
quiz.dataset.done = '1';
score.hidden = false;
score.textContent = right ? 'Correct! Score: 1 / 1'
: 'Not quite — the answer is highlighted.';
});
});
Type the missing word
The learner types an answer and checks it. Try it — the answer is “flexbox”.
CSS lays items out in a row or column.
HTML
<div class="ed-fb">
<p>CSS <input id="fbInput" type="text" aria-label="missing word" placeholder="?">
lays items out in a row or column.
<button class="btn check" id="fbCheck">Check</button></p>
<div class="fb" id="fbMsg" hidden></div>
</div>
CSS
.ed-fb { color: #374151; font-size: 1rem; }
.ed-fb input { border: none; border-bottom: 2px solid #f59e0b; background: #fff;
font-size: 1rem; padding: 2px 8px; width: 130px; text-align: center; }
.ed-fb .check { margin-left: 8px; }
.ed-fb .fb { margin-top: 10px; font-weight: 700; font-size: .9rem; }
.ed-fb .fb.ok { color: #166534; }
.ed-fb .fb.no { color: #991b1b; }
.btn { background: #f59e0b; color: #1a1205; border: none; border-radius: 8px;
padding: 9px 16px; font-weight: 700; cursor: pointer; font-size: .88rem; }
JavaScript
const check = document.getElementById('fbCheck');
const ans = document.getElementById('fbInput');
const msg = document.getElementById('fbMsg');
check.addEventListener('click', () => {
const ok = ans.value.trim().toLowerCase() === 'flexbox';
msg.hidden = false;
msg.textContent = ok ? '✓ Correct!' : '✗ Try again — hint: starts with "flex".';
msg.className = 'fb ' + (ok ? 'ok' : 'no');
});
Pair terms with meanings
Click a term, then its match. Correct pairs lock in green. Match all three.
HTML
<div class="ed-match" id="match"> <button data-key="html">HTML</button><button data-key="css">paints colors & layout</button> <button data-key="css">CSS</button><button data-key="js">adds interactivity</button> <button data-key="js">JavaScript</button><button data-key="html">structures content</button> </div>
CSS
.ed-match { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; max-width: 460px; }
.ed-match button { background: #fff; border: 1px solid #cbd5e1; border-radius: 8px;
padding: 10px 12px; font-size: .88rem; color: #374151;
cursor: pointer; text-align: left; }
.ed-match button.sel { border-color: #f59e0b; background: #fff7ed; }
.ed-match button.done { background: #dcfce7; border-color: #22c55e; color: #166534; cursor: default; }
JavaScript — core idea
const match = document.getElementById('match');
const buttons = match.querySelectorAll('button');
let first = null;
buttons.forEach(b => b.addEventListener('click', () => {
if (b.classList.contains('done')) return; // already matched
if (!first) { first = b; b.classList.add('sel'); return; }
if (first === b) { first.classList.remove('sel'); first = null; return; }
if (first.dataset.key === b.dataset.key) {
first.classList.add('done'); b.classList.add('done'); // match!
}
first.classList.remove('sel'); first = null;
}));
data-key; a match is when two clicked buttons have the same key.Drag each label to its category
Drag the chips into the matching box. HTML → Structure, CSS → Style, JS → Behavior. Built with the native HTML5 drag-and-drop API.
HTML
<div class="ed-dd" id="dd">
<div class="pool">
<div class="chip" draggable="true" data-key="structure">HTML</div>
<div class="chip" draggable="true" data-key="style">CSS</div>
<div class="chip" draggable="true" data-key="behavior">JavaScript</div>
</div>
<div class="targets">
<div class="drop" data-key="structure">Structure</div>
<div class="drop" data-key="style">Style</div>
<div class="drop" data-key="behavior">Behavior</div>
</div>
</div>
CSS
.ed-dd { display: grid; gap: 14px; }
.ed-dd .pool { display: flex; gap: 10px; flex-wrap: wrap; }
.ed-dd .chip { background: #fff; border: 1px solid #cbd5e1; border-radius: 8px;
padding: 8px 14px; font-weight: 700; color: #374151; cursor: grab; }
.ed-dd .targets { display: grid; grid-template-columns: repeat(3,1fr); gap: 10px; }
.ed-dd .drop { border: 2px dashed #cbd5e1; border-radius: 10px; padding: 16px 10px;
text-align: center; font-size: .82rem; color: #6b7280; min-height: 84px; }
.ed-dd .drop.over { border-color: #f59e0b; background: #fff7ed; }
.ed-dd .drop.filled { border-style: solid; border-color: #22c55e; background: #dcfce7;
color: #166534; font-weight: 700; }
JavaScript — native drag & drop
const dd = document.getElementById('dd');
// each chip carries its key (and its text) when you start dragging
dd.querySelectorAll('.chip').forEach(chip => {
chip.addEventListener('dragstart', e => {
e.dataTransfer.setData('key', chip.dataset.key);
e.dataTransfer.setData('txt', chip.textContent);
});
});
dd.querySelectorAll('.drop').forEach(drop => {
drop.addEventListener('dragover', e => { e.preventDefault(); drop.classList.add('over'); }); // allow drop
drop.addEventListener('dragleave', () => drop.classList.remove('over'));
drop.addEventListener('drop', e => {
e.preventDefault(); drop.classList.remove('over');
if (e.dataTransfer.getData('key') === drop.dataset.key) { // correct box!
drop.classList.add('filled');
drop.textContent = e.dataTransfer.getData('txt') + ' ✓';
}
});
});
Check off steps, watch progress fill
A checklist tied to a progress bar — great for course modules or onboarding. Tick the boxes.
HTML
<div class="ed-pt" id="pt"> <div class="pct" id="ptPct">0% complete</div> <div class="barwrap"><div class="barfill" id="ptBar"></div></div> <label><input type="checkbox"> Watch the intro video</label> <label><input type="checkbox"> Read the lesson</label> <label><input type="checkbox"> Complete the exercise</label> <label><input type="checkbox"> Take the quiz</label> </div>
CSS
.ed-pt { max-width: 420px; }
.ed-pt .barwrap { background: #e5e7eb; border-radius: 999px; height: 12px;
overflow: hidden; margin-bottom: 12px; }
.ed-pt .barfill { height: 100%; width: 0;
background: linear-gradient(90deg,#f59e0b,#22c55e); transition: width .3s; }
.ed-pt label { display: flex; align-items: center; gap: 10px; padding: 8px 0;
font-size: .9rem; color: #374151; }
.ed-pt .pct { font-weight: 700; color: #b45309; margin-bottom: 8px; }
JavaScript
const pt = document.getElementById('pt');
const bar = document.getElementById('ptBar');
const label = document.getElementById('ptPct');
const boxes = pt.querySelectorAll('input');
function update() {
const done = [...boxes].filter(b => b.checked).length;
const pct = Math.round(done / boxes.length * 100);
bar.style.width = pct + '%';
label.textContent = pct + '% complete';
}
boxes.forEach(b => b.addEventListener('change', update));
update();
Spot the bug
A challenge asks the learner to apply a skill, not just recall a fact. Here: click the line of CSS with the mistake.
.card { display: flex; flex-direction: colmun; gap: 12px;}
Click the buggy line.
HTML — each line is a clickable block span (kept on one line so there are no blank gaps)
<div class="ed-lc" id="lcChallenge">
<pre class="snip"><span class="line" data-bug="false">.card {</span><span class="line" data-bug="false"> display: flex;</span><span class="line" data-bug="true"> flex-direction: colmun;</span><span class="line" data-bug="false"> gap: 12px;</span><span class="line" data-bug="false">}</span></pre>
<p class="res" id="lcRes">Click the buggy line.</p>
</div>
CSS
.ed-lc { max-width: 460px; }
.ed-lc pre.snip { background: #0d1117; color: #e6edf3; border-radius: 10px;
padding: 14px 16px; font-family: 'SF Mono', Consolas, monospace;
font-size: .82rem; line-height: 1.7; margin: 0 0 12px; white-space: pre; }
.ed-lc .line { display: block; cursor: pointer; border-radius: 5px; padding: 0 6px; }
.ed-lc .line:hover { background: rgba(245,158,11,.18); }
.ed-lc .line.right { background: rgba(34,197,94,.25); }
.ed-lc .line.wrong { background: rgba(239,68,68,.25); }
.ed-lc .res { font-weight: 700; color: #0b3d91; }
JavaScript
const lc = document.getElementById('lcChallenge');
const result = document.getElementById('lcRes');
const lines = lc.querySelectorAll('.line');
lines.forEach(line => line.addEventListener('click', () => {
if (lc.dataset.done) return; // solved already
const correct = line.dataset.bug === 'true';
line.classList.add(correct ? 'right' : 'wrong');
if (correct) lc.dataset.done = '1';
result.textContent = correct
? 'Yes! "colmun" should be "column".'
: 'Not that line — look again.';
}));
Browse modules and lessons
A two-pane viewer: modules with their lessons on the left, the selected lesson on the right. Open a module and pick a lesson.
Tags & Elements
HTML is made of elements written with tags like <p> and <h1>.
HTML
<div class="ed-cm" id="cmViewer">
<div class="mods">
<div class="mod open">
<button class="mh"><i class="ph ph-folder-simple"></i> 1 · HTML Basics</button>
<div class="ls">
<button data-t="Tags & Elements" data-d="HTML is made of elements written with tags like <p> and <h1>.">Tags & Elements</button>
<button data-t="Links & Images" data-d="Use <a> for links and <img> for images, each with the right attributes.">Links & Images</button>
</div>
</div>
<div class="mod">
<button class="mh"><i class="ph ph-folder-simple"></i> 2 · CSS Basics</button>
<div class="ls">
<button data-t="Selectors" data-d="Selectors target elements — by tag, class (.box), or id (#main).">Selectors</button>
<button data-t="The Box Model" data-d="Every element is content, padding, border, and margin.">The Box Model</button>
</div>
</div>
</div>
<div class="view">
<h4 id="cmTitle">Tags & Elements</h4>
<p id="cmBody">HTML is made of elements written with tags like <p> and <h1>.</p>
</div>
</div>
CSS
.ed-cm { display: grid; grid-template-columns: 220px 1fr; gap: 16px; }
.ed-cm .mods { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; }
.ed-cm .mod > .mh { width: 100%; text-align: left; background: none; border: 0;
padding: 11px 14px; font-weight: 700; font-size: .85rem; color: #111;
cursor: pointer; display: flex; align-items: center; gap: 8px;
border-bottom: 1px solid #eef2f6; }
.ed-cm .mod .ls { display: none; }
.ed-cm .mod.open .ls { display: block; }
.ed-cm .mod .ls button { width: 100%; text-align: left; background: none; border: 0;
padding: 9px 14px 9px 32px; font-size: .82rem; color: #374151;
cursor: pointer; border-bottom: 1px solid #f3f4f6; }
.ed-cm .mod .ls button:hover { background: #fff7ed; }
.ed-cm .mod .ls button.active { background: #fff7ed; color: #b45309; font-weight: 700; }
.ed-cm .view { background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 18px; }
.ed-cm .view h4 { color: #111; margin-bottom: 6px; }
.ed-cm .view p { color: #6b7280; font-size: .86rem; }
JavaScript
const cm = document.getElementById('cmViewer');
const title = document.getElementById('cmTitle');
const body = document.getElementById('cmBody');
// click a module header to expand/collapse its lessons
cm.querySelectorAll('.mod .mh').forEach(moduleHead => {
moduleHead.addEventListener('click', () => moduleHead.parentElement.classList.toggle('open'));
});
// click a lesson to load it into the right-hand detail pane
cm.querySelectorAll('.ls button').forEach(lessonBtn => {
lessonBtn.addEventListener('click', () => {
cm.querySelectorAll('.ls button').forEach(b => b.classList.remove('active'));
lessonBtn.classList.add('active');
title.textContent = lessonBtn.dataset.t;
body.textContent = lessonBtn.dataset.d; // swap the detail pane
});
});
Expand a week to see its plan
A syllabus laid out by week — click a week to reveal its topics and readings. It's an accordion with a week label and a list inside.
- What the web is & how pages load
- Setting up VS Code
- Reading: MDN "Getting started"
- Semantic elements
- Links, images, lists
- Assignment: mark up a recipe
- Selectors & the box model
- Flexbox basics
- Project: style your recipe
HTML
<div class="ed-syl" id="sylExplorer">
<div class="wk open">
<button class="wh"><span class="wk-tag">Week 1</span> Foundations <i class="ph ph-caret-down car"></i></button>
<div class="wb"><ul><li>What the web is & how pages load</li><li>Setting up VS Code</li><li>Reading: MDN "Getting started"</li></ul></div>
</div>
<div class="wk">
<button class="wh"><span class="wk-tag">Week 2</span> HTML Structure <i class="ph ph-caret-down car"></i></button>
<div class="wb"><ul><li>Semantic elements</li><li>Links, images, lists</li><li>Assignment: mark up a recipe</li></ul></div>
</div>
<div class="wk">
<button class="wh"><span class="wk-tag">Week 3</span> Styling with CSS <i class="ph ph-caret-down car"></i></button>
<div class="wb"><ul><li>Selectors & the box model</li><li>Flexbox basics</li><li>Project: style your recipe</li></ul></div>
</div>
</div>
CSS
.ed-syl .wk { border: 1px solid #e5e7eb; border-radius: 10px; margin-bottom: 8px;
background: #fff; overflow: hidden; }
.ed-syl .wh { width: 100%; text-align: left; background: none; border: 0;
padding: 12px 14px; font-weight: 700; font-size: .88rem; color: #111;
cursor: pointer; display: flex; align-items: center; gap: 10px; }
.ed-syl .wh .wk-tag { background: #fef3c7; color: #b45309; font-size: .68rem;
font-weight: 800; padding: 3px 8px; border-radius: 999px; }
.ed-syl .wh .car { margin-left: auto; transition: transform .2s; color: #9ca3af; }
.ed-syl .wk.open .wh .car { transform: rotate(180deg); }
.ed-syl .wb { display: none; padding: 0 16px 14px 16px; }
.ed-syl .wk.open .wb { display: block; }
.ed-syl .wb li { font-size: .84rem; color: #374151; margin: 5px 0 5px 18px; }
JavaScript
const syl = document.getElementById('sylExplorer');
// click a week header to toggle its body — same accordion pattern as an FAQ
syl.querySelectorAll('.wh').forEach(weekHead => {
weekHead.addEventListener('click', () => weekHead.parentElement.classList.toggle('open'));
});
/* CSS shows .wb only when its .wk has .open */
Question now, answer on tap
A study guide hides the answers so learners can quiz themselves first. Click a card to reveal the answer.
display: flex; on the parent element.HTML
<div class="ed-sg" id="studyGuide">
<div class="qa">
<div class="q"><i class="ph ph-caret-right"></i> What does the box model include?</div>
<div class="a">Content, padding, border, and margin — from the inside out.</div>
</div>
<div class="qa">
<div class="q"><i class="ph ph-caret-right"></i> What's the difference between margin and padding?</div>
<div class="a">Padding is space inside the border; margin is space outside it.</div>
</div>
<div class="qa">
<div class="q"><i class="ph ph-caret-right"></i> Which display value makes a flex container?</div>
<div class="a"><code>display: flex;</code> on the parent element.</div>
</div>
</div>
CSS
.ed-sg { display: grid; gap: 10px; max-width: 560px; }
.ed-sg .qa { background: #fff; border: 1px solid #e5e7eb; border-radius: 10px;
padding: 14px 16px; cursor: pointer; }
.ed-sg .qa .q { font-weight: 700; color: #111; font-size: .9rem;
display: flex; align-items: center; gap: 8px; }
.ed-sg .qa .q i { color: #f59e0b; transition: transform .2s; }
.ed-sg .qa.open .q i { transform: rotate(90deg); }
.ed-sg .qa .a { font-size: .85rem; color: #6b7280; margin-top: 8px; display: none; }
.ed-sg .qa.open .a { display: block; }
JavaScript
const sg = document.getElementById('studyGuide');
// click a card to reveal/hide its answer
sg.querySelectorAll('.qa').forEach(card => {
card.addEventListener('click', () => card.classList.toggle('open'));
});
/* .a (the answer) is display:none until the card has .open */
Answer all, then submit for a score
Unlike the single quiz, a practice exam collects several answers and grades them together. Pick an answer for each, then Submit.
1. Which tag creates the largest heading?
2. Which property sets text colour?
3. Which unit is relative to the root font size?
HTML
<div class="ed-exam" id="exam">
<div class="q" data-a="1"><p>1. Which tag creates the largest heading?</p>
<button class="opt"><h6></button><button class="opt"><h1></button><button class="opt"><head></button></div>
<div class="q" data-a="0"><p>2. Which property sets text colour?</p>
<button class="opt">color</button><button class="opt">font</button><button class="opt">background</button></div>
<div class="q" data-a="2"><p>3. Which unit is relative to the root font size?</p>
<button class="opt">px</button><button class="opt">%</button><button class="opt">rem</button></div>
<button class="submit" id="examSubmit">Submit exam</button>
<div class="result" id="examResult" hidden></div>
</div>
CSS
.ed-exam .q { margin-bottom: 16px; }
.ed-exam .q p { font-weight: 700; color: #111; margin-bottom: 8px; font-size: .9rem; }
.ed-exam .opt { display: block; width: 100%; text-align: left; background: #fff;
border: 1px solid #cbd5e1; border-radius: 8px; padding: 9px 13px;
margin: 5px 0; cursor: pointer; font-size: .86rem; color: #374151; }
.ed-exam .opt.sel { border-color: #f59e0b; background: #fff7ed; }
.ed-exam .opt.correct { background: #dcfce7; border-color: #22c55e; color: #166534; }
.ed-exam .opt.wrong { background: #fee2e2; border-color: #ef4444; color: #991b1b; }
.ed-exam .submit { background: #f59e0b; color: #1a1205; border: 0; border-radius: 8px;
padding: 10px 20px; font-weight: 800; cursor: pointer; }
.ed-exam .result { margin-top: 12px; font-weight: 800; font-size: 1rem; color: #0b3d91; }
JavaScript — select answers, then grade them all
const exam = document.getElementById('exam');
const submit = document.getElementById('examSubmit');
const result = document.getElementById('examResult');
const questions = [...exam.querySelectorAll('.q')];
// 1) clicking an option selects it (one selection per question)
questions.forEach(q => {
q.querySelectorAll('.opt').forEach(opt => {
opt.addEventListener('click', () => {
if (exam.dataset.done) return; // locked after submit
q.querySelectorAll('.opt').forEach(x => x.classList.remove('sel'));
opt.classList.add('sel');
});
});
});
// 2) submit grades every question at once
submit.addEventListener('click', () => {
if (exam.dataset.done) return;
exam.dataset.done = '1';
let score = 0;
questions.forEach(q => {
const picked = q.querySelector('.opt.sel');
const correct = q.querySelectorAll('.opt')[+q.dataset.a];
correct.classList.add('correct');
if (picked === correct) score++;
else if (picked) picked.classList.add('wrong');
});
result.hidden = false;
result.textContent = `You scored ${score} / ${questions.length}`;
});
Term on front, meaning on back
A grid of vocabulary cards — click any card to flip it and check the definition. Great for key terms in any subject.
Click a term to flip it.
HTML
<p class="ed-vocab-hint">Click a term to flip it.</p> <div class="ed-vocab" id="vocab"> <div class="ed-vcard"><div class="inner"><div class="f front">Element</div><div class="f back">A single HTML building block, like a paragraph or image.</div></div></div> <div class="ed-vcard"><div class="inner"><div class="f front">Selector</div><div class="f back">The part of a CSS rule that targets which elements to style.</div></div></div> <div class="ed-vcard"><div class="inner"><div class="f front">Attribute</div><div class="f back">Extra info on a tag, like href on a link.</div></div></div> <div class="ed-vcard"><div class="inner"><div class="f front">Viewport</div><div class="f back">The visible area of the page in the browser window.</div></div></div> </div>
CSS — the flip (same trick as flash cards)
.ed-vocab { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; }
.ed-vcard { perspective: 800px; height: 110px; cursor: pointer; }
.ed-vcard .inner { position: relative; width: 100%; height: 100%;
transform-style: preserve-3d; transition: transform .5s; }
.ed-vcard.flip .inner { transform: rotateY(180deg); }
.ed-vcard .f { position: absolute; inset: 0; backface-visibility: hidden;
border-radius: 12px; display: grid; place-items: center;
text-align: center; padding: 10px; }
.ed-vcard .front { background: #fff; border: 1px solid #e5e7eb; color: #111;
font-weight: 800; font-size: .95rem; }
.ed-vcard .back { background: #f59e0b; color: #1a1205; transform: rotateY(180deg);
font-size: .78rem; font-weight: 600; line-height: 1.4; } /* pre-rotated so it reads correctly */
.ed-vocab-hint { font-size: .8rem; color: #6b7280; margin-bottom: 12px; }
JavaScript
const vocab = document.getElementById('vocab');
// click any card to flip it (CSS does the 3D rotation)
vocab.querySelectorAll('.ed-vcard').forEach(card => {
card.addEventListener('click', () => card.classList.toggle('flip'));
});
Keep building
See Interactive Features, Gamification, and Timeline — or browse the full Component Library.