🎮ArcadeLab

Kanban Challenge - Park(ing) Day

by PixelPirate96
873 lines20.0 KB
▶ Play
<!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.