Claude Opus 5.5
by Anthropicreasoning defaultClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 56.7 s
- Cost
- $0.153
- Output tokens
- 7,493
- Spent thinking
- 329 tokens
- Speed
- 139.8 tok/s
- Console errors
- 0
- First token after
- 3.2 s
- First visible text after
- 6.2 s
- Prompt tokens
- 745
- Code size
- 16.4 KB
- Finish reason
- stop
- Served by
- Claude Platform on AWS
- Model id
anthropic/claude-opus-5.5- Reasoning sent
model default- Max output
- 128,000 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 · 323 lines
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Kanban Board</title>
<style>
:root{
--bg:#f3f4f8; --panel:#e9ebf2; --card:#ffffff; --text:#1d2130; --muted:#6b7183;
--border:#d8dbe5; --accent:#5b5bf0; --accent-soft:rgba(91,91,240,.12);
--shadow:0 1px 2px rgba(20,25,45,.08),0 2px 8px rgba(20,25,45,.06);
--low:#1f9d6b; --low-bg:#dcf5ea; --med:#b7791f; --med-bg:#fdf0d5; --high:#d93a4a; --high-bg:#fde0e3;
}
[data-theme="dark"]{
--bg:#12141c; --panel:#1b1e29; --card:#252937; --text:#e7e9f1; --muted:#9097ab;
--border:#323748; --accent:#8b8bff; --accent-soft:rgba(139,139,255,.15);
--shadow:0 1px 2px rgba(0,0,0,.4),0 4px 12px rgba(0,0,0,.25);
--low:#4fd6a0; --low-bg:rgba(79,214,160,.14); --med:#f2b955; --med-bg:rgba(242,185,85,.14); --high:#ff7584; --high-bg:rgba(255,117,132,.14);
}
*{box-sizing:border-box}
html,body{margin:0}
body{font-family:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;background:var(--bg);color:var(--text);min-height:100vh;transition:background .25s,color .25s;overflow-x:hidden}
header{display:flex;align-items:center;gap:12px;flex-wrap:wrap;padding:18px 28px;border-bottom:1px solid var(--border)}
h1{font-size:20px;margin:0;margin-right:auto;display:flex;align-items:center;gap:10px}
h1 .logo{width:28px;height:28px;border-radius:8px;background:linear-gradient(135deg,var(--accent),#c05bf0);display:inline-grid;place-items:center;color:#fff;font-size:15px}
.search{position:relative}
.search input{width:240px;max-width:100%;padding:9px 12px 9px 34px;border-radius:10px;border:1px solid var(--border);background:var(--card);color:var(--text);font-size:14px;outline:none}
.search input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
.search svg{position:absolute;left:10px;top:50%;transform:translateY(-50%);color:var(--muted)}
button{font:inherit;cursor:pointer}
.btn{padding:9px 14px;border-radius:10px;border:1px solid var(--border);background:var(--card);color:var(--text);font-size:14px;font-weight:500}
.btn:hover{border-color:var(--accent)}
.btn.primary{background:var(--accent);color:#fff;border-color:var(--accent)}
.btn.primary:hover{filter:brightness(1.08)}
main{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px;padding:24px 28px;align-items:start}
.column{background:var(--panel);border-radius:14px;padding:14px;display:flex;flex-direction:column;min-height:300px;border:2px solid transparent;transition:border-color .15s,background .15s}
.column.over{border-color:var(--accent);background:var(--accent-soft)}
.col-head{display:flex;align-items:center;gap:8px;padding:2px 4px 12px}
.dot{width:10px;height:10px;border-radius:50%}
.col-head h2{font-size:14px;margin:0;text-transform:uppercase;letter-spacing:.05em}
.count{margin-left:auto;font-size:12px;font-weight:600;background:var(--card);color:var(--muted);padding:2px 9px;border-radius:20px;border:1px solid var(--border)}
.list{display:flex;flex-direction:column;gap:10px;flex:1;min-height:80px}
.card{background:var(--card);border-radius:10px;padding:12px 12px 10px;box-shadow:var(--shadow);border:1px solid var(--border);cursor:grab;user-select:none;position:relative;transition:transform .12s,box-shadow .12s,opacity .12s;animation:pop .2s ease}
.card:hover{transform:translateY(-1px)}
.card:active{cursor:grabbing}
.card.dragging{opacity:.35;transform:scale(.98)}
@keyframes pop{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.card-title{font-size:14px;line-height:1.4;margin:0 24px 10px 0;word-break:break-word}
.card-meta{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--muted)}
.badge{font-size:11px;font-weight:700;padding:3px 8px;border-radius:6px;text-transform:uppercase;letter-spacing:.04em}
.badge.low{color:var(--low);background:var(--low-bg)}
.badge.medium{color:var(--med);background:var(--med-bg)}
.badge.high{color:var(--high);background:var(--high-bg)}
.del{position:absolute;top:8px;right:8px;width:24px;height:24px;border-radius:6px;border:none;background:transparent;color:var(--muted);font-size:16px;line-height:1;display:grid;place-items:center;opacity:.5}
.card:hover .del,.del:focus{opacity:1}
.del:hover{background:var(--high-bg);color:var(--high)}
.placeholder{height:54px;border-radius:10px;border:2px dashed var(--accent);background:var(--accent-soft);flex-shrink:0;position:relative}
.placeholder::before{content:"";position:absolute;left:-6px;top:50%;width:8px;height:8px;border-radius:50%;background:var(--accent);transform:translateY(-50%)}
.empty{font-size:13px;color:var(--muted);text-align:center;padding:20px 8px;border:1px dashed var(--border);border-radius:10px}
.add-inline{margin-top:10px;background:transparent;border:none;color:var(--muted);text-align:left;padding:8px 6px;border-radius:8px;font-size:13px}
.add-inline:hover{background:var(--card);color:var(--text)}
mark{background:rgba(255,210,0,.45);color:inherit;border-radius:3px;padding:0 1px}
.overlay{position:fixed;inset:0;background:rgba(10,12,20,.5);display:none;align-items:center;justify-content:center;padding:16px;z-index:10;backdrop-filter:blur(2px)}
.overlay.open{display:flex}
.modal{background:var(--card);color:var(--text);border-radius:14px;padding:22px;width:100%;max-width:400px;box-shadow:0 20px 50px rgba(0,0,0,.3);animation:pop .18s ease}
.modal h3{margin:0 0 16px;font-size:18px}
.field{margin-bottom:14px}
.field label{display:block;font-size:13px;font-weight:600;margin-bottom:6px;color:var(--muted)}
.field input,.field select{width:100%;padding:10px 12px;border-radius:9px;border:1px solid var(--border);background:var(--bg);color:var(--text);font-size:14px;outline:none}
.field input:focus,.field select:focus{border-color:var(--accent)}
.field input.invalid{border-color:var(--high)}
.err{color:var(--high);font-size:12px;margin-top:5px;display:none}
.err.show{display:block}
.prio-group{display:flex;gap:8px}
.prio-group label{flex:1;margin:0;cursor:pointer}
.prio-group input{display:none}
.prio-group span{display:block;text-align:center;padding:8px;border-radius:8px;border:1px solid var(--border);font-size:13px;font-weight:600;color:var(--text)}
.prio-group input:checked + span.low{background:var(--low-bg);color:var(--low);border-color:var(--low)}
.prio-group input:checked + span.medium{background:var(--med-bg);color:var(--med);border-color:var(--med)}
.prio-group input:checked + span.high{background:var(--high-bg);color:var(--high);border-color:var(--high)}
.actions{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}
.hint{text-align:center;color:var(--muted);font-size:12px;padding:0 20px 20px}
@media (max-width:820px){
main{grid-template-columns:1fr;padding:16px}
header{padding:14px 16px}
.search{flex:1 1 100%;order:3}
.search input{width:100%}
.column{min-height:auto}
}
</style>
</head>
<body>
<header>
<h1><span class="logo">▦</span>Product Sprint</h1>
<div class="search">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<input id="search" type="search" placeholder="Search cards…" aria-label="Search cards by title">
</div>
<button class="btn" id="themeBtn" aria-label="Toggle theme">🌙 Dark</button>
<button class="btn primary" id="addBtn">+ Add card</button>
</header>
<main id="board"></main>
<p class="hint">Drag cards between columns to change their status.</p>
<div class="overlay" id="overlay">
<form class="modal" id="form" novalidate>
<h3>New card</h3>
<div class="field">
<label for="fTitle">Title *</label>
<input id="fTitle" maxlength="120" placeholder="e.g. Write release notes" autocomplete="off">
<div class="err" id="fErr">Title is required.</div>
</div>
<div class="field">
<label>Priority</label>
<div class="prio-group">
<label><input type="radio" name="prio" value="low"><span class="low">Low</span></label>
<label><input type="radio" name="prio" value="medium" checked><span class="medium">Medium</span></label>
<label><input type="radio" name="prio" value="high"><span class="high">High</span></label>
</div>
</div>
<div class="field">
<label for="fCol">Column</label>
<select id="fCol"></select>
</div>
<div class="actions">
<button type="button" class="btn" id="cancelBtn">Cancel</button>
<button type="submit" class="btn primary">Add card</button>
</div>
</form>
</div>
<script>
(function(){
const COLUMNS = [
{id:"todo", name:"To do", color:"#8b93a7"},
{id:"progress", name:"In progress", color:"#5b5bf0"},
{id:"done", name:"Done", color:"#1f9d6b"}
];
let nextId = 1;
const mk = (title, priority, col) => ({id: nextId++, title, priority, col});
let cards = [
mk("Design onboarding email sequence","medium","todo"),
mk("Fix checkout crash on Safari 17","high","todo"),
mk("Update API rate-limit docs","low","todo"),
mk("Migrate user table to Postgres 16","high","progress"),
mk("Add dark mode to settings page","medium","progress"),
mk("Set up CI caching for builds","low","done")
];
let query = "";
let dragId = null;
const board = document.getElementById("board");
const placeholder = document.createElement("div");
placeholder.className = "placeholder";
const esc = s => s.replace(/[&<>"']/g, c => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c]));
function highlight(title){
if(!query) return esc(title);
const i = title.toLowerCase().indexOf(query);
if(i < 0) return esc(title);
return esc(title.slice(0,i)) + "<mark>" + esc(title.slice(i,i+query.length)) + "</mark>" + esc(title.slice(i+query.length));
}
function render(){
board.innerHTML = "";
COLUMNS.forEach(col => {
const all = cards.filter(c => c.col === col.id);
const shown = all.filter(c => c.title.toLowerCase().includes(query));
const sec = document.createElement("section");
sec.className = "column";
sec.dataset.col = col.id;
sec.innerHTML = `
<div class="col-head">
<span class="dot" style="background:${col.color}"></span>
<h2>${col.name}</h2>
<span class="count" title="Cards in column">${query ? shown.length + " / " + all.length : all.length}</span>
</div>
<div class="list"></div>
<button class="add-inline" type="button">+ Add a card</button>`;
const list = sec.querySelector(".list");
if(!shown.length){
const e = document.createElement("div");
e.className = "empty";
e.textContent = query ? "No matching cards" : "Drop cards here";
list.appendChild(e);
}
shown.forEach(c => {
const el = document.createElement("article");
el.className = "card";
el.draggable = true;
el.dataset.id = c.id;
el.innerHTML = `
<p class="card-title">${highlight(c.title)}</p>
<div class="card-meta"><span class="badge ${c.priority}">${c.priority}</span><span>#${String(c.id).padStart(3,"0")}</span></div>
<button class="del" type="button" aria-label="Delete card" title="Delete">×</button>`;
el.querySelector(".del").addEventListener("click", ev => {
ev.stopPropagation();
el.style.transition="opacity .15s, transform .15s";
el.style.opacity="0"; el.style.transform="scale(.95)";
setTimeout(() => { cards = cards.filter(x => x.id !== c.id); render(); }, 150);
});
el.addEventListener("dragstart", ev => {
dragId = c.id;
ev.dataTransfer.effectAllowed = "move";
ev.dataTransfer.setData("text/plain", String(c.id));
requestAnimationFrame(() => el.classList.add("dragging"));
});
el.addEventListener("dragend", cleanupDrag);
list.appendChild(el);
});
sec.querySelector(".add-inline").addEventListener("click", () => openModal(col.id));
sec.addEventListener("dragover", ev => {
if(dragId === null) return;
ev.preventDefault();
ev.dataTransfer.dropEffect = "move";
document.querySelectorAll(".column.over").forEach(x => x !== sec && x.classList.remove("over"));
sec.classList.add("over");
const after = getAfter(list, ev.clientY);
const empty = list.querySelector(".empty");
if(empty) empty.style.display = "none";
if(after) list.insertBefore(placeholder, after); else list.appendChild(placeholder);
});
sec.addEventListener("dragleave", ev => {
if(!sec.contains(ev.relatedTarget)){
sec.classList.remove("over");
if(placeholder.parentNode === list) placeholder.remove();
const empty = list.querySelector(".empty");
if(empty) empty.style.display = "";
}
});
sec.addEventListener("drop", ev => {
ev.preventDefault();
if(dragId === null) return;
const after = placeholder.parentNode === list ? placeholder.nextElementSibling : null;
const card = cards.find(x => x.id === dragId);
cards = cards.filter(x => x.id !== dragId);
card.col = col.id;
let beforeId = null, n = after;
while(n && !(n.classList && n.classList.contains("card") && +n.dataset.id !== dragId)) n = n.nextElementSibling;
if(n) beforeId = +n.dataset.id;
if(beforeId !== null){
cards.splice(cards.findIndex(x => x.id === beforeId), 0, card);
} else {
// insert after last card of this column
let lastIdx = -1;
cards.forEach((x,i) => { if(x.col === col.id) lastIdx = i; });
cards.splice(lastIdx + 1, 0, card);
}
cleanupDrag();
render();
});
board.appendChild(sec);
});
}
function getAfter(list, y){
const els = [...list.querySelectorAll(".card:not(.dragging)")];
for(const el of els){
const r = el.getBoundingClientRect();
if(y < r.top + r.height/2) return el;
}
return null;
}
function cleanupDrag(){
dragId = null;
placeholder.remove();
document.querySelectorAll(".dragging").forEach(x => x.classList.remove("dragging"));
document.querySelectorAll(".column.over").forEach(x => x.classList.remove("over"));
document.querySelectorAll(".empty").forEach(x => x.style.display = "");
}
// Search
document.getElementById("search").addEventListener("input", e => {
query = e.target.value.trim().toLowerCase();
render();
});
// Theme
const themeBtn = document.getElementById("themeBtn");
if(window.matchMedia && matchMedia("(prefers-color-scheme: dark)").matches) setTheme("dark");
function setTheme(t){
document.documentElement.dataset.theme = t;
themeBtn.textContent = t === "dark" ? "☀️ Light" : "🌙 Dark";
}
themeBtn.addEventListener("click", () => setTheme(document.documentElement.dataset.theme === "dark" ? "light" : "dark"));
// Modal
const overlay = document.getElementById("overlay");
const form = document.getElementById("form");
const fTitle = document.getElementById("fTitle");
const fErr = document.getElementById("fErr");
const fCol = document.getElementById("fCol");
fCol.innerHTML = COLUMNS.map(c => `<option value="${c.id}">${c.name}</option>`).join("");
function openModal(colId){
form.reset();
fCol.value = colId || "todo";
fTitle.classList.remove("invalid"); fErr.classList.remove("show");
overlay.classList.add("open");
setTimeout(() => fTitle.focus(), 20);
}
function closeModal(){ overlay.classList.remove("open"); }
document.getElementById("addBtn").addEventListener("click", () => openModal("todo"));
document.getElementById("cancelBtn").addEventListener("click", closeModal);
overlay.addEventListener("mousedown", e => { if(e.target === overlay) closeModal(); });
document.addEventListener("keydown", e => { if(e.key === "Escape" && overlay.classList.contains("open")) closeModal(); });
fTitle.addEventListener("input", () => { if(fTitle.value.trim()){ fTitle.classList.remove("invalid"); fErr.classList.remove("show"); } });
form.addEventListener("submit", e => {
e.preventDefault();
const title = fTitle.value.trim();
if(!title){ fTitle.classList.add("invalid"); fErr.classList.add("show"); fTitle.focus(); return; }
const priority = form.querySelector('input[name="prio"]:checked').value;
cards.push(mk(title, priority, fCol.value));
closeModal();
render();
});
render();
})();
</script>
</body>
</html>Reasoning trace (329 tokens)
I'm weighing whether to use pointer-based dragging or HTML5 drag-and-drop for the Kanban board. HTML5 drag-and-drop is fine with a mouse in Chrome, but I'm also considering whether touch support is worth adding through pointer events, and whether keyboard move buttons should be included while keeping the code concise. I'm sketching the state as an array of cards, each with an id, title, priority, and column, and I'm about to decide how cards are ordered within a column. The state is a plain array, and cards are ordered within a column by their position in that array. Next I'm sketching the drag handling: on dragstart I record the dragged id, and while dragging over a column I work out the insertion point from the mouse height against the card midpoints and show a placeholder there. On drop the card just moves in the array. I'm also planning a modal for title, priority, and column, with Esc to close, a delete button, a title search that shows filtered over total counts, a theme toggle through data-theme on the html element, and a three-column grid that stacks under 800px.