Muse Ember
by MetaReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 28.1 s
- Cost
- $0.0078
- Output tokens
- 15,488
- Spent thinking
- 6,937 tokens
- Speed
- 592.4 tok/s
- Console errors
- 0
- First token after
- 1.9 s
- First visible text after
- 18.0 s
- Prompt tokens
- 511
- Code size
- 27.9 KB
- Finish reason
- stop
- Served by
- Meta
- Model id
meta/muse-ember- Reasoning sent
{"effort":"max"}- Max output
- 943,718 tokens
- Generated
- Nov 12, 2026
What it was asked
The prompt
Build an interactive Kanban board in vanilla HTML, CSS and JavaScript (no framework, no library). Requirements: - Three columns: "To do", "In progress", "Done", each showing its card count. - Pre-populate the board with 6 realistic cards spread across the columns so the first screenshot looks alive. - Each card has a title and a priority (low / medium / high) shown as a colored badge. - Add a card through a small modal form (title required), and delete a card with a button on it. - Drag and drop cards between columns with the mouse, with a clear visual drop indicator. - A search field that filters cards live by title. - A light / dark theme toggle. - State lives in memory only (no storage APIs).
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task. Output rules: - Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block. - Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task. - Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw. - The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar. - Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction. - Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session. - Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing. - The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message. - The code must run with zero errors in the console of the latest Chrome.
Code run-1.html · 563 lines
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kanban Board</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#eef1f7;
--surface:#ffffff;
--surface-2:#f8fafc;
--column-bg:#e9eef6;
--card-bg:#ffffff;
--text:#0f172a;
--muted:#64748b;
--border:#e2e8f0;
--input-bg:#ffffff;
--shadow:0 10px 25px rgba(15,23,42,.08);
--shadow-sm:0 2px 8px rgba(15,23,42,.06);
--primary:#6366f1;
--primary-hover:#4f46e5;
}
[data-theme="dark"]{
--bg:#0b0f1a;
--surface:#151b2e;
--surface-2:#1e293b;
--column-bg:#131a2e;
--card-bg:#1a2238;
--text:#e8ecf5;
--muted:#94a3b8;
--border:#263052;
--input-bg:#1a2238;
--shadow:0 14px 30px rgba(0,0,0,.35);
--shadow-sm:0 2px 10px rgba(0,0,0,.25);
}
html{overflow-x:hidden}
body{
font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Inter,Arial,sans-serif;
background:var(--bg);
color:var(--text);
min-height:100vh;
padding:0 22px 28px;
transition:background .25s,color .25s;
overflow-x:hidden;
}
body::before{content:"";display:block;height:5px;margin:0 -22px 0;background:linear-gradient(90deg,#6366f1,#a855f7,#ec4899,#f59e0b);border-radius:0 0 99px 99px}
/* Topbar */
.topbar{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;padding:20px 0 16px;max-width:1240px;margin:0 auto}
.brand{display:flex;align-items:center;gap:13px;min-width:0}
.logo{width:46px;height:46px;border-radius:13px;background:linear-gradient(135deg,#6366f1,#8b5cf6);display:flex;align-items:center;justify-content:center;color:#fff;flex-shrink:0;box-shadow:0 8px 20px rgba(99,102,241,.35)}
.logo svg{width:26px;height:26px}
.brand h1{font-size:22px;line-height:1.1;letter-spacing:-.02em}
.brand p{font-size:13px;color:var(--muted);margin-top:3px}
.controls{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.search-wrap{position:relative;width:280px;max-width:100%}
.search-wrap svg.search-icon{position:absolute;left:13px;top:50%;transform:translateY(-50%);width:17px;height:17px;color:var(--muted);pointer-events:none}
.search-wrap input{width:100%;height:42px;border:1.5px solid var(--border);background:var(--input-bg);color:var(--text);border-radius:99px;padding:0 36px 0 39px;font-size:14px;outline:none;transition:.2s;box-shadow:var(--shadow-sm)}
.search-wrap input:focus{border-color:var(--primary);box-shadow:0 0 0 4px rgba(99,102,241,.15)}
.search-wrap input::placeholder{color:var(--muted)}
.clear-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:26px;height:26px;border:none;border-radius:50%;background:var(--surface-2);color:var(--muted);cursor:pointer;font-size:16px;line-height:1;display:none}
.clear-btn:hover{background:var(--border);color:var(--text)}
.icon-btn{width:42px;height:42px;border-radius:50%;border:1.5px solid var(--border);background:var(--surface);color:var(--text);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.2s;box-shadow:var(--shadow-sm);flex-shrink:0}
.icon-btn:hover{transform:translateY(-1px);border-color:var(--primary)}
.icon-btn svg{width:19px;height:19px}
[data-theme="light"] .icon-sun{display:none} [data-theme="dark"] .icon-moon{display:none}
.add-btn{height:42px;border:none;border-radius:99px;background:linear-gradient(135deg,#6366f1,#7c3aed);color:#fff;font-size:14px;font-weight:700;padding:0 18px;cursor:pointer;display:inline-flex;align-items:center;gap:7px;box-shadow:0 8px 18px rgba(99,102,241,.32);transition:.2s;white-space:nowrap}
.add-btn:hover{background:var(--primary-hover);transform:translateY(-1px)}
.add-btn svg{width:16px;height:16px}
/* Summary */
.summary{max-width:1240px;margin:0 auto 18px;background:var(--surface);border:1px solid var(--border);border-radius:16px;padding:14px 18px;display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap;box-shadow:var(--shadow-sm)}
.summary strong{font-size:15px}
.summary span.sub{display:block;font-size:13px;color:var(--muted);margin-top:2px}
.progress-area{display:flex;align-items:center;gap:12px;min-width:260px;flex:1;max-width:420px}
.track{flex:1;height:10px;border-radius:99px;background:var(--surface-2);border:1px solid var(--border);overflow:hidden}
.fill{height:100%;width:0%;background:linear-gradient(90deg,#22c55e,#10b981);border-radius:99px;transition:width .35s ease}
.pct{font-weight:800;font-size:14px;min-width:42px;text-align:right;color:#16a34a}
/* Board */
.board{max-width:1240px;margin:0 auto;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;align-items:start}
.column{background:var(--column-bg);border:1px solid var(--border);border-radius:18px;padding:14px;min-width:0;transition:border-color .2s,box-shadow .2s}
.column.drag-over{border:2px dashed var(--primary);box-shadow:0 0 0 5px rgba(99,102,241,.12)}
.col-head{display:flex;align-items:center;gap:9px;padding:4px 4px 12px}
.col-dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}
.todo-dot{background:#3b82f6;box-shadow:0 0 0 4px rgba(59,130,246,.15)}
.progress-dot{background:#f59e0b;box-shadow:0 0 0 4px rgba(245,158,11,.15)}
.done-dot{background:#22c55e;box-shadow:0 0 0 4px rgba(34,197,94,.15)}
.col-head h2{font-size:15px;letter-spacing:-.01em;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.count{font-size:12px;font-weight:800;background:var(--surface);border:1px solid var(--border);color:var(--muted);padding:3px 9px;border-radius:99px;min-width:28px;text-align:center}
.col-label{font-size:12px;color:var(--muted);font-weight:600}
.col-add{width:28px;height:28px;border-radius:50%;border:1px solid var(--border);background:var(--surface);color:var(--muted);cursor:pointer;font-size:18px;line-height:1;display:flex;align-items:center;justify-content:center;transition:.2s;flex-shrink:0}
.col-add:hover{background:var(--primary);color:#fff;border-color:var(--primary)}
.card-list{display:flex;flex-direction:column;gap:11px;min-height:200px}
.card{background:var(--card-bg);border:1px solid var(--border);border-radius:14px;padding:13px 14px 12px;box-shadow:var(--shadow-sm);cursor:grab;transition:transform .15s,box-shadow .15s,border-color .15s;animation:fadeUp .35s ease both;position:relative}
.card:hover{transform:translateY(-2px);box-shadow:var(--shadow);border-color:#c7d2fe}
.card:active{cursor:grabbing}
.card.dragging{opacity:.35;transform:rotate(1.5deg) scale(.98)}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.card-top{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:9px}
.badge{display:inline-flex;align-items:center;gap:6px;font-size:11px;font-weight:800;letter-spacing:.04em;text-transform:uppercase;padding:4px 9px;border-radius:99px}
.badge .dot{width:6px;height:6px;border-radius:50%;background:currentColor}
.badge-low{background:#dcfce7;color:#166534}.badge-medium{background:#fef3c7;color:#92400e}.badge-high{background:#fee2e2;color:#991b1b}
[data-theme="dark"] .badge-low{background:rgba(34,197,94,.18);color:#86efac}
[data-theme="dark"] .badge-medium{background:rgba(245,158,11,.18);color:#fcd34d}
[data-theme="dark"] .badge-high{background:rgba(239,68,68,.18);color:#fca5a5}
.delete-btn{width:26px;height:26px;border:none;border-radius:50%;background:transparent;color:var(--muted);font-size:18px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:.15s;flex-shrink:0}
.delete-btn:hover{background:#fee2e2;color:#dc2626;transform:scale(1.08)}
.card-title{font-size:15px;line-height:1.35;letter-spacing:-.01em;font-weight:650;margin-bottom:12px;word-break:break-word}
.card-meta{display:flex;align-items:center;justify-content:space-between;gap:8px}
.tag{font-size:12px;color:var(--muted);background:var(--surface-2);border:1px solid var(--border);padding:3px 8px;border-radius:99px;font-weight:600}
.avatar{width:27px;height:27px;border-radius:50%;background:linear-gradient(135deg,#6366f1,#a855f7);color:#fff;font-size:10px;font-weight:800;display:flex;align-items:center;justify-content:center;letter-spacing:.02em;flex-shrink:0}
.empty{border:1.5px dashed var(--border);border-radius:12px;padding:22px 14px;text-align:center;color:var(--muted);font-size:13px;line-height:1.4;background:transparent}
.drop-indicator{height:5px;border-radius:99px;background:var(--primary);box-shadow:0 0 0 4px rgba(99,102,241,.18),0 4px 10px rgba(99,102,241,.4);margin:2px 0;flex-shrink:0}
.hint{max-width:1240px;margin:16px auto 0;text-align:center;font-size:12.5px;color:var(--muted)}
kbd{background:var(--surface);border:1px solid var(--border);border-bottom-width:2px;border-radius:6px;padding:1px 6px;font-size:11px;font-family:inherit;font-weight:700;color:var(--text)}
/* Modal */
.overlay{position:fixed;inset:0;background:rgba(8,10,20,.55);backdrop-filter:blur(6px);display:flex;align-items:center;justify-content:center;padding:18px;opacity:0;pointer-events:none;transition:opacity .2s;z-index:50}
.overlay.open{opacity:1;pointer-events:auto}
.modal{width:100%;max-width:440px;background:var(--surface);color:var(--text);border:1px solid var(--border);border-radius:20px;padding:22px;box-shadow:0 25px 60px rgba(0,0,0,.25);transform:translateY(14px) scale(.98);transition:.22s}
.overlay.open .modal{transform:translateY(0) scale(1)}
.modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:6px}
.modal-head h3{font-size:19px;letter-spacing:-.02em}
.modal-head p{font-size:13px;color:var(--muted);margin-top:4px}
.close-x{width:34px;height:34px;border-radius:50%;border:1px solid var(--border);background:var(--surface-2);color:var(--muted);cursor:pointer;font-size:19px;line-height:1;flex-shrink:0}
.close-x:hover{color:var(--text)}
.field{margin-top:16px}
.field label{display:block;font-size:13px;font-weight:700;margin-bottom:7px}
.field label em{color:#dc2626;font-style:normal}
.field input,.field select{width:100%;height:44px;border:1.5px solid var(--border);border-radius:11px;background:var(--input-bg);color:var(--text);padding:0 13px;font-size:14px;outline:none;transition:.2s;font-family:inherit}
.field input:focus,.field select:focus{border-color:var(--primary);box-shadow:0 0 0 4px rgba(99,102,241,.13)}
.field input.invalid{border-color:#ef4444;box-shadow:0 0 0 4px rgba(239,68,68,.12)}
.error{display:none;color:#dc2626;font-size:12.5px;font-weight:600;margin-top:6px}
.error.show{display:block}
.prio-row{display:flex;gap:8px}
.prio-btn{flex:1;height:40px;border-radius:11px;border:1.5px solid var(--border);background:var(--surface-2);color:var(--muted);font-weight:800;font-size:13px;cursor:pointer;text-transform:capitalize;transition:.15s;font-family:inherit}
.prio-btn.selected[data-priority="low"]{background:#dcfce7;border-color:#86efac;color:#166534}
.prio-btn.selected[data-priority="medium"]{background:#fef3c7;border-color:#fcd34d;color:#92400e}
.prio-btn.selected[data-priority="high"]{background:#fee2e2;border-color:#fca5a5;color:#991b1b}
.modal-actions{display:flex;gap:10px;margin-top:22px}
.btn-cancel{flex:1;height:44px;border-radius:12px;border:1.5px solid var(--border);background:var(--surface);color:var(--text);font-weight:700;font-size:14px;cursor:pointer;font-family:inherit}
.btn-cancel:hover{background:var(--surface-2)}
.btn-submit{flex:1.4;height:44px;border:none;border-radius:12px;background:linear-gradient(135deg,#6366f1,#7c3aed);color:#fff;font-weight:800;font-size:14px;cursor:pointer;font-family:inherit;box-shadow:0 8px 18px rgba(99,102,241,.3)}
.btn-submit:hover{filter:brightness(1.06)}
/* Toast */
.toast{position:fixed;left:50%;bottom:22px;transform:translate(-50%,20px);background:#0f172a;color:#fff;font-size:13.5px;font-weight:700;padding:11px 18px;border-radius:99px;box-shadow:0 12px 30px rgba(0,0,0,.25);opacity:0;pointer-events:none;transition:.25s;z-index:60;white-space:nowrap;max-width:calc(100vw - 32px);overflow:hidden;text-overflow:ellipsis}
.toast.show{opacity:1;transform:translate(-50%,0)}
@media (max-width:960px){
.board{grid-template-columns:1fr}
.card-list{min-height:90px}
.search-wrap{flex:1;min-width:220px}
}
@media (max-width:520px){
body{padding:0 14px 24px} body::before{margin:0 -14px 0}
.topbar{padding-top:16px}.brand h1{font-size:19px}
.controls{width:100%}.search-wrap{width:100%;flex-basis:100%}
.add-btn{flex:1;justify-content:center}
.progress-area{max-width:none;width:100%}
.summary{padding:13px 14px}
.prio-row{flex-direction:row}
}
</style>
</head>
<body>
<header class="topbar">
<div class="brand">
<div class="logo" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><rect x="3" y="4" width="5" height="13" rx="1.5"/><rect x="10" y="4" width="5" height="9" rx="1.5"/><rect x="17" y="4" width="4" height="16" rx="1.5"/></svg>
</div>
<div>
<h1>Kanban Board</h1>
<p>Plan · Track · Ship</p>
</div>
</div>
<div class="controls">
<div class="search-wrap">
<svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
<input id="searchInput" type="search" placeholder="Search cards... ( / )" aria-label="Search cards by title" autocomplete="off">
<button class="clear-btn" id="clearSearch" type="button" aria-label="Clear search">×</button>
</div>
<button class="icon-btn" id="themeToggle" type="button" aria-label="Toggle light / dark theme" title="Toggle theme">
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/></svg>
</button>
<button class="add-btn" id="addCardBtn" type="button">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
Add card
</button>
</div>
</header>
<div class="summary">
<div>
<strong id="summaryText">2 of 6 tasks done</strong>
<span class="sub">Drag cards between columns to update progress</span>
</div>
<div class="progress-area">
<div class="track"><div class="fill" id="progressFill"></div></div>
<span class="pct" id="progressPct">33%</span>
</div>
</div>
<main class="board" id="board">
<section class="column" data-col="todo">
<div class="col-head">
<span class="col-dot todo-dot"></span>
<h2>To do</h2>
<span class="col-label" id="count-todo">2</span>
<button class="col-add" data-col="todo" type="button" aria-label="Add card to To do" title="Add to To do">+</button>
</div>
<div class="card-list" data-col="todo"></div>
</section>
<section class="column" data-col="progress">
<div class="col-head">
<span class="col-dot progress-dot"></span>
<h2>In progress</h2>
<span class="col-label" id="count-progress">2</span>
<button class="col-add" data-col="progress" type="button" aria-label="Add card to In progress" title="Add to In progress">+</button>
</div>
<div class="card-list" data-col="progress"></div>
</section>
<section class="column" data-col="done">
<div class="col-head">
<span class="col-dot done-dot"></span>
<h2>Done</h2>
<span class="col-label" id="count-done">2</span>
<button class="col-add" data-col="done" type="button" aria-label="Add card to Done" title="Add to Done">+</button>
</div>
<div class="card-list" data-col="done"></div>
</section>
</main>
<p class="hint">Tip: Drag cards between columns · Press <kbd>/</kbd> to search · <kbd>Esc</kbd> closes the form</p>
<!-- Modal -->
<div class="overlay" id="modalOverlay" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<div class="modal">
<div class="modal-head">
<div>
<h3 id="modalTitle">Add a new card</h3>
<p>Give your task a clear title and set its priority.</p>
</div>
<button class="close-x" id="closeModal" type="button" aria-label="Close">×</button>
</div>
<form id="cardForm" novalidate>
<div class="field">
<label for="titleInput">Title <em>*</em></label>
<input id="titleInput" type="text" placeholder="e.g. Fix login bug" maxlength="90" autocomplete="off">
<p class="error" id="errorMsg">Please enter a title for this card.</p>
</div>
<div class="field">
<label>Priority</label>
<div class="prio-row">
<button class="prio-btn" type="button" data-priority="low">Low</button>
<button class="prio-btn selected" type="button" data-priority="medium">Medium</button>
<button class="prio-btn" type="button" data-priority="high">High</button>
</div>
</div>
<div class="field">
<label for="columnSelect">Column</label>
<select id="columnSelect">
<option value="todo">To do</option>
<option value="progress">In progress</option>
<option value="done">Done</option>
</select>
</div>
<div class="modal-actions">
<button class="btn-cancel" id="cancelBtn" type="button">Cancel</button>
<button class="btn-submit" type="submit">Add card</button>
</div>
</form>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
(function(){
"use strict";
var state = [
{id:"1", title:"Design new landing page hero", priority:"high", column:"todo", tag:"Design", assignee:"AK"},
{id:"2", title:"Prepare Q3 marketing report", priority:"medium", column:"todo", tag:"Marketing", assignee:"JM"},
{id:"3", title:"Fix checkout payment bug", priority:"high", column:"progress", tag:"Engineering", assignee:"RS"},
{id:"4", title:"Implement user onboarding flow", priority:"medium", column:"progress", tag:"Product", assignee:"AK"},
{id:"5", title:"Update brand guidelines v2", priority:"low", column:"done", tag:"Design", assignee:"LT"},
{id:"6", title:"Refactor API authentication", priority:"low", column:"done", tag:"Engineering", assignee:"RS"}
];
var query = "";
var draggingId = null;
var selectedPriority = "medium";
var columns = ["todo","progress","done"];
var searchInput = document.getElementById("searchInput");
var clearSearch = document.getElementById("clearSearch");
var themeToggle = document.getElementById("themeToggle");
var addCardBtn = document.getElementById("addCardBtn");
var overlay = document.getElementById("modalOverlay");
var closeModalBtn = document.getElementById("closeModal");
var cancelBtn = document.getElementById("cancelBtn");
var form = document.getElementById("cardForm");
var titleInput = document.getElementById("titleInput");
var columnSelect = document.getElementById("columnSelect");
var errorMsg = document.getElementById("errorMsg");
var toast = document.getElementById("toast");
var toastTimer = null;
var dropIndicator = document.createElement("div");
dropIndicator.className = "drop-indicator";
function showToast(msg){
toast.textContent = msg;
toast.classList.add("show");
if(toastTimer) clearTimeout(toastTimer);
toastTimer = setTimeout(function(){ toast.classList.remove("show"); }, 2200);
}
function createCard(card, idx){
var el = document.createElement("article");
el.className = "card";
el.draggable = true;
el.dataset.id = card.id;
el.style.animationDelay = (idx * 50) + "ms";
var top = document.createElement("div");
top.className = "card-top";
var badge = document.createElement("span");
badge.className = "badge badge-" + card.priority;
var dot = document.createElement("span");
dot.className = "dot";
badge.appendChild(dot);
badge.appendChild(document.createTextNode(card.priority));
var del = document.createElement("button");
del.className = "delete-btn";
del.type = "button";
del.title = "Delete card";
del.setAttribute("aria-label", "Delete card: " + card.title);
del.textContent = "\u00D7";
del.addEventListener("click", function(e){
e.stopPropagation();
state = state.filter(function(c){ return c.id !== card.id; });
render();
showToast("Card deleted");
});
top.appendChild(badge);
top.appendChild(del);
var title = document.createElement("h3");
title.className = "card-title";
title.textContent = card.title;
var meta = document.createElement("div");
meta.className = "card-meta";
var tag = document.createElement("span");
tag.className = "tag";
tag.textContent = card.tag || "General";
var avatar = document.createElement("span");
avatar.className = "avatar";
avatar.textContent = card.assignee || "YO";
avatar.title = card.assignee || "";
meta.appendChild(tag);
meta.appendChild(avatar);
el.appendChild(top);
el.appendChild(title);
el.appendChild(meta);
return el;
}
function render(){
columns.forEach(function(col){
var list = document.querySelector('.card-list[data-col="'+col+'"]');
var countEl = document.getElementById("count-" + col);
if(!list) return;
list.innerHTML = "";
var inCol = state.filter(function(c){ return c.column === col; });
var visible = inCol.filter(function(c){ return c.title.toLowerCase().indexOf(query) !== -1; });
if(countEl) countEl.textContent = inCol.length;
visible.forEach(function(card, i){ list.appendChild(createCard(card, i)); });
if(visible.length === 0){
var empty = document.createElement("div");
empty.className = "empty";
empty.textContent = query ? "No matching cards" : "No cards here \u2014 drag one in";
list.appendChild(empty);
}
});
var total = state.length;
var done = state.filter(function(c){ return c.column === "done"; }).length;
var pct = total ? Math.round((done / total) * 100) : 0;
document.getElementById("summaryText").textContent = done + " of " + total + " tasks done";
document.getElementById("progressFill").style.width = pct + "%";
document.getElementById("progressPct").textContent = pct + "%";
}
/* Drag & drop */
function cleanupDrag(){
dropIndicator.remove();
document.querySelectorAll(".column").forEach(function(c){ c.classList.remove("drag-over"); });
document.querySelectorAll(".card.dragging").forEach(function(c){ c.classList.remove("dragging"); });
}
function getAfter(list, y){
var cards = Array.prototype.slice.call(list.querySelectorAll(".card:not(.dragging)"));
for(var i=0;i<cards.length;i++){
var r = cards[i].getBoundingClientRect();
if(y <= r.top + r.height / 2) return cards[i];
}
return null;
}
function moveCard(id, targetCol, beforeId){
if(!id) return;
var from = -1;
for(var i=0;i<state.length;i++) if(state[i].id === id){ from = i; break; }
if(from === -1) return;
var moved = state.splice(from,1)[0];
moved.column = targetCol;
var insertAt;
if(beforeId){
insertAt = state.length;
for(var j=0;j<state.length;j++) if(state[j].id === beforeId){ insertAt = j; break; }
} else {
var last = -1;
for(var k=0;k<state.length;k++) if(state[k].column === targetCol) last = k;
insertAt = (last === -1) ? state.length : last + 1;
}
state.splice(insertAt, 0, moved);
}
document.querySelectorAll(".card-list").forEach(function(list){
list.addEventListener("dragstart", function(e){
var card = e.target.closest(".card");
if(!card) return;
draggingId = card.dataset.id;
card.classList.add("dragging");
if(e.dataTransfer){
e.dataTransfer.effectAllowed = "move";
try{ e.dataTransfer.setData("text/plain", draggingId); }catch(err){}
}
});
list.addEventListener("dragover", function(e){
e.preventDefault();
if(e.dataTransfer) e.dataTransfer.dropEffect = "move";
var after = getAfter(list, e.clientY);
if(after) list.insertBefore(dropIndicator, after);
else list.appendChild(dropIndicator);
var col = list.closest(".column");
if(col) col.classList.add("drag-over");
});
list.addEventListener("dragleave", function(e){
if(!list.contains(e.relatedTarget)){
dropIndicator.remove();
var col = list.closest(".column");
if(col) col.classList.remove("drag-over");
}
});
list.addEventListener("drop", function(e){
e.preventDefault();
var next = dropIndicator.nextElementSibling;
var beforeId = (next && next.classList.contains("card")) ? next.dataset.id : null;
var col = list.dataset.col;
var id = draggingId;
cleanupDrag();
draggingId = null;
moveCard(id, col, beforeId);
render();
});
list.addEventListener("dragend", function(){
cleanupDrag();
draggingId = null;
});
});
/* Search */
searchInput.addEventListener("input", function(){
query = searchInput.value.trim().toLowerCase();
clearSearch.style.display = searchInput.value ? "block" : "none";
render();
});
clearSearch.addEventListener("click", function(){
searchInput.value = "";
query = "";
clearSearch.style.display = "none";
render();
searchInput.focus();
});
/* Theme */
themeToggle.addEventListener("click", function(){
var root = document.documentElement;
var next = root.getAttribute("data-theme") === "dark" ? "light" : "dark";
root.setAttribute("data-theme", next);
showToast(next === "dark" ? "Dark theme on" : "Light theme on");
});
/* Modal */
function setPriority(p){
selectedPriority = p;
document.querySelectorAll(".prio-btn").forEach(function(b){
b.classList.toggle("selected", b.dataset.priority === p);
});
}
document.querySelectorAll(".prio-btn").forEach(function(b){
b.addEventListener("click", function(){ setPriority(b.dataset.priority); });
});
function openModal(col){
columnSelect.value = col || "todo";
titleInput.value = "";
titleInput.classList.remove("invalid");
errorMsg.classList.remove("show");
setPriority("medium");
overlay.classList.add("open");
setTimeout(function(){ titleInput.focus(); }, 60);
}
function closeModal(){
overlay.classList.remove("open");
}
addCardBtn.addEventListener("click", function(){ openModal("todo"); });
document.querySelectorAll(".col-add").forEach(function(b){
b.addEventListener("click", function(){ openModal(b.dataset.col); });
});
closeModalBtn.addEventListener("click", closeModal);
cancelBtn.addEventListener("click", closeModal);
overlay.addEventListener("click", function(e){ if(e.target === overlay) closeModal(); });
titleInput.addEventListener("input", function(){
if(titleInput.value.trim()){ titleInput.classList.remove("invalid"); errorMsg.classList.remove("show"); }
});
form.addEventListener("submit", function(e){
e.preventDefault();
var title = titleInput.value.trim();
if(!title){
titleInput.classList.add("invalid");
errorMsg.classList.add("show");
titleInput.focus();
return;
}
state.push({
id: "c" + Date.now(),
title: title,
priority: selectedPriority,
column: columnSelect.value,
tag: "General",
assignee: "YO"
});
closeModal();
render();
showToast("Card added");
});
/* Keyboard */
document.addEventListener("keydown", function(e){
var tag = (e.target.tagName || "").toLowerCase();
var typing = tag === "input" || tag === "textarea" || tag === "select";
if(e.key === "Escape"){ closeModal(); return; }
if(e.key === "/" && !typing){ e.preventDefault(); searchInput.focus(); }
});
render();
})();
</script>
</body>
</html>Reasoning trace (6,937 tokens)
Implementing vanilla HTML, CSS, and JavaScript Kanban board with drag-drop, modal form, search, theme toggle, and in-memory state.Extending the implementation with sample data, styling, and drag-drop event handling.Adding DOM-based rendering, drag-drop indicators, modal form validation, and theme toggle with responsive CSS.Implementing drag-and-drop state updates with column highlighting and modal form validation.Reusing a single drop indicator to prevent flicker and adding render logic with search filtering and empty states.Extending rendering with tag chips, avatars, theme toggle, modal, and responsive CSS and JS.Adding drag-and-drop handlers with drop indicator logic and empty-state edge cases.Finalizing UI styling, SVG icons, layout, animations, and initial state rendering.