Kanban Challenge - Park(ing) Day
by PixelPirate96873 lines20.0 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kanban Challenge - Park(ing) Day</title>
<style>
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
background: #f7f7f8;
color: #242424;
}
.container {
max-width: 1180px;
margin: 0 auto;
padding: 28px 20px 50px;
}
/* Scenario */
.scenario {
background: #eee5fa;
border-left: 5px solid #8b45b8;
border-radius: 0 12px 12px 0;
padding: 16px 20px;
margin-bottom: 18px;
color: #4d276d;
line-height: 1.5;
font-size: 14px;
}
.scenario strong {
color: #512378;
}
/* Score */
.score-box {
background: #eee5fa;
border-left: 4px solid #8b45b8;
border-radius: 10px;
padding: 12px 18px;
margin-bottom: 20px;
color: #67348a;
font-size: 14px;
}
.score-number {
font-weight: bold;
color: #542275;
}
/* Main board */
.game-board {
display: grid;
grid-template-columns: 190px 1fr 1fr 1fr 1fr;
gap: 12px;
align-items: stretch;
}
/* Tasks to sort */
.task-pool {
background: white;
border-radius: 16px;
padding: 15px;
box-shadow: 0 3px 12px rgba(0,0,0,0.06);
}
.task-pool h3 {
margin: 0;
font-size: 12px;
letter-spacing: 1px;
color: #7d3aa4;
text-transform: uppercase;
}
.pool-line {
height: 1px;
background: #e7dced;
margin: 10px 0 14px;
}
.pool-task {
background: #f7f7f7;
border-radius: 9px;
padding: 12px 10px;
margin-bottom: 8px;
font-size: 12px;
line-height: 1.25;
cursor: grab;
border: 1px solid transparent;
transition: 0.2s;
}
.pool-task:hover {
background: #f1e9f6;
border-color: #c79adf;
transform: translateY(-1px);
}
.pool-task.dragging {
opacity: 0.45;
}
.pool-dot {
display: inline-block;
width: 8px;
height: 8px;
background: #a7d7a8;
border-radius: 50%;
margin-right: 7px;
}
/* Columns */
.column {
background: #fff;
border-radius: 15px;
min-height: 420px;
overflow: hidden;
box-shadow: 0 3px 12px rgba(0,0,0,0.05);
}
.column-header {
padding: 13px 13px 11px;
border-top: 4px solid;
}
.column.backlog .column-header {
border-color: #999;
}
.column.priority .column-header {
border-color: #ef5558;
}
.column.progress .column-header {
border-color: #4b82e8;
}
.column.done .column-header {
border-color: #3fa052;
}
.column-title {
font-size: 15px;
font-weight: bold;
margin-bottom: 3px;
}
.column-subtitle {
font-size: 9px;
color: #aaa;
text-transform: uppercase;
letter-spacing: .7px;
}
.drop-zone {
padding: 7px 10px 15px;
min-height: 360px;
}
.drop-zone.drag-over {
background: #faf3ff;
outline: 2px dashed #a55ac9;
outline-offset: -5px;
}
/* Cards */
.card {
background: #fff;
border: 1px solid #f05a5d;
border-radius: 9px;
padding: 12px;
margin-bottom: 8px;
font-size: 12px;
line-height: 1.3;
cursor: grab;
transition: .2s;
position: relative;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 9px rgba(0,0,0,.08);
}
.card.dragging {
opacity: .4;
}
.card.correct {
border-color: #6abb72;
background: #f4fbf4;
}
.card.incorrect {
border-color: #ef5558;
background: #fff6f6;
}
.card-number {
position: absolute;
right: 8px;
top: 7px;
color: #e94b4e;
font-weight: bold;
font-size: 10px;
}
.recommended {
display: none;
border: 1px dashed #61a969;
background: #f2faf2;
color: #418349;
border-radius: 8px;
padding: 10px;
margin-bottom: 8px;
font-size: 11px;
cursor: pointer;
}
.recommended.show {
display: block;
}
.recommended span {
float: right;
background: #d7efd8;
padding: 3px 5px;
border-radius: 4px;
font-size: 9px;
font-weight: bold;
}
/* Buttons */
.controls {
display: flex;
justify-content: center;
gap: 10px;
margin-top: 18px;
}
button {
border: none;
border-radius: 9px;
padding: 12px 28px;
font-weight: bold;
cursor: pointer;
font-size: 14px;
}
.reveal-btn {
background: #d9c0ed;
color: white;
flex: 1;
max-width: 520px;
}
.reveal-btn:hover {
background: #c9a9e0;
}
.reset-btn {
background: white;
color: #7d3aa4;
border: 1px solid #7d3aa4;
}
.reset-btn:hover {
background: #f4ebf8;
}
/* Reasoning section */
.reasoning {
background: white;
margin-top: 25px;
border-radius: 15px;
padding: 18px;
box-shadow: 0 3px 12px rgba(0,0,0,.06);
display: none;
}
.reasoning.show {
display: block;
}
.reasoning-title {
font-size: 12px;
text-transform: uppercase;
letter-spacing: 1px;
color: #7d3aa4;
margin-bottom: 15px;
font-weight: bold;
}
.reasoning-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 9px;
}
.reason-card {
background: #fffaf2;
border-radius: 11px;
padding: 14px;
}
.reason-card h4 {
margin: 0 0 5px;
font-size: 13px;
}
.reason-status {
font-weight: bold;
font-size: 11px;
margin-bottom: 8px;
}
.priority-text {
color: #ef5558;
}
.backlog-text {
color: #777;
}
.progress-text {
color: #417ee0;
}
.done-text {
color: #42a052;
}
.reason-card p {
margin: 0;
font-size: 11px;
line-height: 1.45;
color: #555;
}
/* Success */
.success {
display: none;
text-align: center;
margin-top: 18px;
padding: 14px;
border-radius: 10px;
background: #eaf7eb;
color: #31783a;
font-weight: bold;
}
.success.show {
display: block;
}
/* Mobile */
@media (max-width: 900px) {
.game-board {
grid-template-columns: 1fr 1fr;
}
.task-pool {
grid-column: 1 / -1;
}
}
@media (max-width: 600px) {
.game-board {
grid-template-columns: 1fr;
}
.task-pool {
grid-column: auto;
}
.reasoning-grid {
grid-template-columns: 1fr;
}
.controls {
flex-direction: column;
}
.reveal-btn {
max-width: none;
}
}
</style>
</head>
<body>
<div class="container">
<!-- Scenario -->
<div class="scenario">
<strong>Scenario:</strong>
Tanja wants to organise a Park(ing) Day outside her school — she will block off a parking space for a day and host a furniture-building workshop with her classmates. She has already found a carpenter friend to lead the workshop. Help sort the remaining tasks into the right Kanban columns, then reveal her board to compare.
</div>
<!-- Score -->
<div class="score-box">
<span class="score-number" id="score">0/6 correct.</span>
Drag the tasks into the Kanban columns, then click <strong>Reveal</strong> to check your answer.
</div>
<!-- Board -->
<div class="game-board">
<!-- TASK POOL -->
<div class="task-pool" id="taskPool">
<h3>Tasks to Sort</h3>
<div class="pool-line"></div>
<div class="pool-task" draggable="true" data-task="parking">
<span class="pool-dot"></span>
Contact municipality for parking permission
</div>
<div class="pool-task" draggable="true" data-task="snacks">
<span class="pool-dot"></span>
Organise snacks and drinks
</div>
<div class="pool-task" draggable="true" data-task="music">
<span class="pool-dot"></span>
Check permission for music / speakers
</div>
<div class="pool-task" draggable="true" data-task="posters">
<span class="pool-dot"></span>
Making posters
</div>
<div class="pool-task" draggable="true" data-task="instagram">
<span class="pool-dot"></span>
Writing the Instagram post
</div>
<div class="pool-task" draggable="true" data-task="carpenter">
<span class="pool-dot"></span>
Found a carpenter to lead the workshop
</div>
</div>
<!-- BACKLOG -->
<div class="column backlog">
<div class="column-header">
<div class="column-title">Backlog</div>
<div class="column-subtitle">Not started yet</div>
</div>
<div class="drop-zone" data-column="backlog"></div>
</div>
<!-- PRIORITY -->
<div class="column priority">
<div class="column-header">
<div class="column-title">Priority</div>
<div class="column-subtitle">Needs to happen soon</div>
</div>
<div class="drop-zone" data-column="priority"></div>
</div>
<!-- IN PROGRESS -->
<div class="column progress">
<div class="column-header">
<div class="column-title">In Progress</div>
<div class="column-subtitle">Currently being done</div>
</div>
<div class="drop-zone" data-column="progress"></div>
</div>
<!-- DONE -->
<div class="column done">
<div class="column-header">
<div class="column-title">Done</div>
<div class="column-subtitle">Completed</div>
</div>
<div class="drop-zone" data-column="done"></div>
</div>
</div>
<!-- Buttons -->
<div class="controls">
<button class="reveal-btn" onclick="revealBoard()">
✓ Reveal
</button>
<button class="reset-btn" onclick="resetGame()">
↻ Reset
</button>
</div>
<div class="success" id="successMessage">
🎉 Excellent! You sorted all 6 tasks correctly!
</div>
<!-- REASONING -->
<div class="reasoning" id="reasoning">
<div class="reasoning-title">
Tanja's Recommended Board & Reasoning
</div>
<div class="reasoning-grid">
<div class="reason-card">
<h4>Contact municipality for parking permission</h4>
<div class="reason-status priority-text">
→ Priority
</div>
<p>
Getting legal permission to block a parking space is the most critical step — without it, the entire event cannot happen. This must be tackled first.
</p>
</div>
<div class="reason-card">
<h4>Organise snacks and drinks</h4>
<div class="reason-status backlog-text">
→ Backlog
</div>
<p>
Important for the atmosphere, but not time-sensitive yet. It can be planned once the event date and space are confirmed.
</p>
</div>
<div class="reason-card">
<h4>Check permission for music / speakers</h4>
<div class="reason-status priority-text">
→ Priority
</div>
<p>
Another permission that needs to be resolved early so the programme can be planned around whatever is allowed.
</p>
</div>
<div class="reason-card">
<h4>Making posters</h4>
<div class="reason-status progress-text">
→ In Progress
</div>
<p>
Promotional materials need to go up well before the event. Tanja has already started this to build buzz and spread the word around school.
</p>
</div>
<div class="reason-card">
<h4>Writing the Instagram post</h4>
<div class="reason-status backlog-text">
→ Backlog
</div>
<p>
Social media content can be drafted once the key details — date, location and programme — are confirmed. There is no need to rush this before permissions are secured.
</p>
</div>
<div class="reason-card">
<h4>Found a carpenter to lead the workshop</h4>
<div class="reason-status done-text">
→ Done
</div>
<p>
Already completed — the scenario tells us Tanja has already found a carpenter friend. This can be considered finished.
</p>
</div>
</div>
</div>
</div>
<script>
const correctAnswers = {
parking: "priority",
snacks: "backlog",
music: "priority",
posters: "progress",
instagram: "backlog",
carpenter: "done"
};
let draggedElement = null;
let revealed = false;
/* Drag start */
document.addEventListener("dragstart", function(e) {
if (
e.target.classList.contains("pool-task") ||
e.target.classList.contains("card")
) {
draggedElement = e.target;
e.target.classList.add("dragging");
}
});
/* Drag end */
document.addEventListener("dragend", function(e) {
if (e.target.classList.contains("dragging")) {
e.target.classList.remove("dragging");
}
draggedElement = null;
});
/* Drop zones */
document.querySelectorAll(".drop-zone").forEach(zone => {
zone.addEventListener("dragover", function(e) {
e.preventDefault();
zone.classList.add("drag-over");
});
zone.addEventListener("dragleave", function() {
zone.classList.remove("drag-over");
});
zone.addEventListener("drop", function(e) {
e.preventDefault();
zone.classList.remove("drag-over");
if (!draggedElement) return;
moveTaskToColumn(draggedElement, zone);
});
});
/* Task pool can receive tasks again */
const taskPool = document.getElementById("taskPool");
taskPool.addEventListener("dragover", function(e) {
e.preventDefault();
});
taskPool.addEventListener("drop", function(e) {
e.preventDefault();
if (!draggedElement) return;
if (
draggedElement.classList.contains("card") ||
draggedElement.classList.contains("pool-task")
) {
const taskId = draggedElement.dataset.task;
const originalTask = createPoolTask(taskId);
taskPool.appendChild(originalTask);
draggedElement.remove();
}
});
/* Move task */
function moveTaskToColumn(element, zone) {
const taskId = element.dataset.task;
let card;
if (element.classList.contains("pool-task")) {
card = createCard(taskId);
element.remove();
} else {
card = element;
}
zone.appendChild(card);
if (revealed) {
checkSingleCard(card, zone.dataset.column);
}
}
/* Create card */
function createCard(taskId) {
const names = {
parking: "Contact municipality for parking permission",
snacks: "Organise snacks and drinks",
music: "Check permission for music / speakers",
posters: "Making posters",
instagram: "Writing the Instagram post",
carpenter: "Found a carpenter to lead the workshop"
};
const card = document.createElement("div");
card.className = "card";
card.draggable = true;
card.dataset.task = taskId;
card.innerHTML = `
${names[taskId]}
<span class="card-number">u2717</span>
`;
return card;
}
/* Recreate task in pool */
function createPoolTask(taskId) {
const names = {
parking: "Contact municipality for parking permission",
snacks: "Organise snacks and drinks",
music: "Check permission for music / speakers",
posters: "Making posters",
instagram: "Writing the Instagram post",
carpenter: "Found a carpenter to lead the workshop"
};
const task = document.createElement("div");
task.className = "pool-task";
task.draggable = true;
task.dataset.task = taskId;
task.innerHTML = `
<span class="pool-dot"></span>
${names[taskId]}
`;
return task;
}
/* Reveal answer */
function revealBoard() {
revealed = true;
let score = 0;
document.querySelectorAll(".card").forEach(card => {
const zone = card.closest(".drop-zone");
if (!zone) return;
const column = zone.dataset.column;
if (correctAnswers[card.dataset.task] === column) {
card.classList.remove("incorrect");
card.classList.add("correct");
score++;
} else {
card.classList.remove("correct");
card.classList.add("incorrect");
}
});
document.getElementById("score").textContent =
score + "/6 correct.";
document.getElementById("reasoning")
.classList.add("show");
if (score === 6) {
document.getElementById("successMessage")
.classList.add("show");
} else {
document.getElementById("successMessage")
.classList.remove("show");
}
}
/* Check individual card */
function checkSingleCard(card, column) {
if (correctAnswers[card.dataset.task] === column) {
card.classList.add("correct");
card.classList.remove("incorrect");
} else {
card.classList.add("incorrect");
card.classList.remove("correct");
}
}
/* Reset */
function resetGame() {
const taskPool = document.getElementById("taskPool");
const allTasks = [
"parking",
"snacks",
"music",
"posters",
"instagram",
"carpenter"
];
document.querySelectorAll(".drop-zone").forEach(zone => {
zone.innerHTML = "";
});
allTasks.forEach(taskId => {
taskPool.appendChild(createPoolTask(taskId));
});
document.getElementById("score").textContent =
"0/6 correct.";
document.getElementById("reasoning")
.classList.remove("show");
document.getElementById("successMessage")
.classList.remove("show");
revealed = false;
}
</script>
</body>
</html>Game Source: Kanban Challenge - Park(ing) Day
Creator: PixelPirate96
Libraries: none
Complexity: complex (873 lines, 20.0 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: kanban-challenge-park-ing-day-pixelpirate96" to link back to the original. Then publish at arcadelab.ai/publish.