GPT-6 Luna
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 48.7 s
- Cost
- $0.0029
- Output tokens
- 5,717
- Spent thinking
- 63 tokens
- Speed
- 124.8 tok/s
- Console errors
- 0
- First token after
- 2.9 s
- First visible text after
- 4.5 s
- Prompt tokens
- 493
- Code size
- 18.2 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-6-luna- Reasoning sent
{"effort":"low"}- Max output
- 64,000 tokens
- Generated
- Nov 10, 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 · 356 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Boardly — Kanban</title>
<style>
:root {
color-scheme: light;
--bg: #f5f6fa;
--surface: #fff;
--surface-soft: #f8f9fc;
--text: #202435;
--muted: #83899a;
--line: #e9ebf1;
--accent: #6259e8;
--accent-soft: #eeedff;
--shadow: 0 8px 24px rgba(29, 35, 63, .055);
--low: #289c75;
--medium: #d49424;
--high: #df5c67;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 14px;
min-height: 100vh;
transition: background .2s, color .2s;
}
body.dark {
color-scheme: dark;
--bg: #171923;
--surface: #20232f;
--surface-soft: #292c39;
--text: #f0f1f7;
--muted: #a0a5b5;
--line: #343746;
--accent: #9b94ff;
--accent-soft: #343149;
--shadow: 0 8px 24px rgba(0, 0, 0, .18);
--low: #63c99f;
--medium: #f0bb55;
--high: #ff818c;
}
button, input { font: inherit; }
button { color: inherit; }
.app { max-width: 1190px; margin: 0 auto; padding: 0 30px 52px; }
.topbar {
height: 72px; border-bottom: 1px solid var(--line); display: flex;
align-items: center; justify-content: space-between;
}
.brand { display:flex; align-items:center; gap:10px; font-weight:750; letter-spacing:-.4px; font-size:17px; }
.brand-mark {
width:30px;height:30px;border-radius:9px;background:var(--accent);color:white;
display:grid;place-items:center;font-size:16px;box-shadow:0 4px 10px #6259e833;
}
.top-right { display:flex;align-items:center;gap:12px; }
.avatar {
width:32px;height:32px;border-radius:50%;display:grid;place-items:center;
background:#ffe3d5;color:#9f553d;font-size:11px;font-weight:750;border:2px solid var(--surface);
}
.profile { display:flex;align-items:center;gap:8px;font-size:12px;font-weight:650; }
.workspace { color:var(--muted);font-size:12px;margin-right:6px; }
.intro { padding:32px 0 24px; }
.eyebrow { color:var(--accent);font-size:11px;font-weight:750;letter-spacing:1.3px;text-transform:uppercase;margin-bottom:7px; }
h1 { font-size:29px;letter-spacing:-1px;margin:0;font-weight:760; }
.subtitle { color:var(--muted);font-size:13px;margin:7px 0 0; }
.toolbar { display:flex;align-items:center;justify-content:space-between;gap:14px;margin:0 0 21px; }
.toolbar-left,.toolbar-right { display:flex;align-items:center;gap:10px; }
.search-wrap { position:relative; }
.search-wrap svg { position:absolute;left:12px;top:50%;transform:translateY(-50%);color:var(--muted); }
.search {
width:230px;height:39px;border:1px solid var(--line);border-radius:9px;
background:var(--surface);color:var(--text);padding:0 12px 0 36px;outline:none;
transition:border .15s, box-shadow .15s;
}
.search:focus,.field:focus { border-color:var(--accent);box-shadow:0 0 0 3px color-mix(in srgb, var(--accent) 15%, transparent); }
.search::placeholder,.field::placeholder { color:#a2a7b4; }
.button {
border:1px solid var(--line);height:39px;padding:0 13px;border-radius:9px;
background:var(--surface);display:inline-flex;align-items:center;justify-content:center;
gap:8px;font-weight:650;cursor:pointer;transition:transform .15s,background .15s,border .15s;
}
.button:hover { background:var(--surface-soft); }
.button:active { transform:translateY(1px); }
.button.primary { background:var(--accent);border-color:var(--accent);color:#fff;box-shadow:0 3px 9px color-mix(in srgb,var(--accent) 25%,transparent); }
.button.primary:hover { filter:brightness(1.06); }
.icon-button { width:39px;padding:0; }
.board { display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:18px;align-items:start; }
.column {
min-width:0;background:var(--surface-soft);border:1px solid var(--line);
border-radius:12px;padding:13px 12px 12px;min-height:340px;transition:background .16s,border .16s;
}
.column.drag-over { border:1px dashed var(--accent);background:var(--accent-soft); }
.column-head { display:flex;align-items:center;justify-content:space-between;padding:2px 2px 13px; }
.col-label { display:flex;align-items:center;gap:9px;font-size:13px;font-weight:720; }
.dot { width:8px;height:8px;border-radius:50%;background:#9ca2b3; }
.column[data-status="todo"] .dot { background:#9aa0b2; }
.column[data-status="progress"] .dot { background:#ebaa49; }
.column[data-status="done"] .dot { background:#42ad83; }
.count { font-size:11px;font-weight:700;color:var(--muted);background:var(--surface);border:1px solid var(--line);min-width:23px;height:22px;border-radius:7px;display:grid;place-items:center;padding:0 5px; }
.add-inline { border:0;background:none;color:var(--muted);cursor:pointer;width:26px;height:26px;border-radius:7px;font-size:19px;line-height:1; }
.add-inline:hover { color:var(--accent);background:var(--accent-soft); }
.cards { display:flex;flex-direction:column;gap:10px;min-height:274px; }
.card {
background:var(--surface);border:1px solid var(--line);border-radius:10px;
padding:13px 13px 11px;box-shadow:var(--shadow);cursor:grab;transition:opacity .15s,transform .15s,border .15s;
}
.card:hover { border-color:color-mix(in srgb,var(--accent) 30%,var(--line));transform:translateY(-1px); }
.card.dragging { opacity:.35;transform:scale(.98); }
.card-top { display:flex;align-items:flex-start;justify-content:space-between;gap:8px; }
.card-title { font-size:13px;line-height:1.45;font-weight:650;letter-spacing:-.12px; }
.delete {
border:0;background:transparent;color:#a0a4b0;cursor:pointer;width:23px;height:23px;
margin:-4px -5px 0 0;border-radius:6px;display:grid;place-items:center;flex:none;opacity:0;
}
.card:hover .delete,.delete:focus-visible { opacity:1; }
.delete:hover { color:var(--high);background:color-mix(in srgb,var(--high) 12%,transparent); }
.card-bottom { display:flex;align-items:center;justify-content:space-between;margin-top:13px; }
.priority { display:inline-flex;align-items:center;gap:5px;border-radius:5px;padding:4px 7px;font-size:10px;font-weight:700; }
.priority::before { content:"";width:5px;height:5px;border-radius:50%;background:currentColor; }
.priority.low { color:var(--low);background:color-mix(in srgb,var(--low) 12%,transparent); }
.priority.medium { color:var(--medium);background:color-mix(in srgb,var(--medium) 13%,transparent); }
.priority.high { color:var(--high);background:color-mix(in srgb,var(--high) 12%,transparent); }
.meta { color:var(--muted);font-size:10px;display:flex;align-items:center;gap:5px; }
.mini-avatar { width:21px;height:21px;border-radius:50%;display:grid;place-items:center;background:#e6e5ff;color:#6259c9;font-size:9px;font-weight:750; }
.column-empty { color:var(--muted);font-size:12px;text-align:center;padding:26px 8px;border:1px dashed var(--line);border-radius:9px;display:none; }
.empty-search { text-align:center;color:var(--muted);padding:48px 8px;display:none; }
.board-footer { display:flex;justify-content:space-between;color:var(--muted);font-size:11px;padding:15px 2px; }
.footer-key { border:1px solid var(--line);border-radius:4px;padding:2px 5px;background:var(--surface);font-size:10px; }
.modal-backdrop { position:fixed;inset:0;background:rgba(18,20,33,.48);display:none;align-items:center;justify-content:center;padding:18px;z-index:10; }
.modal-backdrop.open { display:flex; }
.modal { width:min(100%,420px);background:var(--surface);border:1px solid var(--line);border-radius:15px;padding:23px;box-shadow:0 20px 60px #0003;animation:pop .16s ease-out; }
@keyframes pop { from { opacity:0;transform:translateY(7px) scale(.99); } to { opacity:1;transform:none; } }
.modal-head { display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:20px; }
.modal h2 { font-size:19px;letter-spacing:-.4px;margin:0 0 5px; }
.modal-sub { color:var(--muted);font-size:12px; }
.modal-close { border:0;background:none;color:var(--muted);font-size:21px;cursor:pointer;width:28px;height:28px;border-radius:6px; }
.modal-close:hover { background:var(--surface-soft); }
.form-row { margin-bottom:15px; }
.form-row label { display:block;font-size:12px;font-weight:650;margin:0 0 7px; }
.field { width:100%;height:40px;border:1px solid var(--line);border-radius:8px;padding:0 11px;background:var(--surface);color:var(--text);outline:none; }
select.field { cursor:pointer; }
.modal-actions { display:flex;justify-content:flex-end;gap:9px;margin-top:22px; }
@media (max-width:760px) {
.app { padding:0 18px 35px; }
.board { grid-template-columns:1fr;gap:12px; }
.column { min-height:0; }
.cards { min-height:55px; }
.intro { padding:26px 0 20px; }
.workspace,.profile-name { display:none; }
.column-empty { padding:17px 8px; }
}
@media (max-width:450px) {
.app { padding-left:14px;padding-right:14px; }
.topbar { height:62px; }
h1 { font-size:25px; }
.toolbar { align-items:stretch; }
.toolbar-left { flex:1; }
.search-wrap { flex:1; }
.search { width:100%;min-width:0; }
.toolbar-right { gap:7px; }
.button { padding:0 10px; }
.button.primary { font-size:12px; }
.board-footer { font-size:10px; }
.delete { opacity:1; }
}
</style>
</head>
<body>
<div class="app">
<header class="topbar">
<div class="brand"><span class="brand-mark">▥</span> boardly</div>
<div class="top-right">
<span class="workspace">Product workspace</span>
<div class="profile"><span class="avatar">AM</span><span class="profile-name">Alex Morgan</span></div>
</div>
</header>
<main>
<section class="intro">
<div class="eyebrow">Team workspace</div>
<h1>Product launch</h1>
<p class="subtitle">Plan, track, and ship great work together.</p>
</section>
<div class="toolbar">
<div class="toolbar-left">
<div class="search-wrap">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"></circle><path d="m20 20-4-4"></path></svg>
<input id="search" class="search" type="search" placeholder="Search tasks..." aria-label="Search tasks">
</div>
</div>
<div class="toolbar-right">
<button class="button icon-button" id="themeToggle" aria-label="Toggle dark theme" title="Toggle theme">
<svg id="themeIcon" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M20.1 15.4A8.5 8.5 0 0 1 8.6 3.9 8.5 8.5 0 1 0 20.1 15.4Z"></path></svg>
</button>
<button class="button primary" id="openModal"><span style="font-size:18px;line-height:0">+</span> Add task</button>
</div>
</div>
<section class="board" id="board" aria-label="Kanban board"></section>
<div class="board-footer">
<span>Drag a card to move it between columns</span>
<span><span class="footer-key">⌘</span> <span class="footer-key">K</span> Search tasks</span>
</div>
</main>
</div>
<div class="modal-backdrop" id="backdrop">
<form class="modal" id="taskForm">
<div class="modal-head">
<div><h2>Add a task</h2><div class="modal-sub">Add a new item to your board.</div></div>
<button type="button" class="modal-close" id="closeModal" aria-label="Close">×</button>
</div>
<div class="form-row">
<label for="taskTitle">Task title</label>
<input class="field" id="taskTitle" maxlength="90" placeholder="What needs to be done?" required>
</div>
<div class="form-row">
<label for="taskPriority">Priority</label>
<select class="field" id="taskPriority">
<option value="medium">Medium</option><option value="low">Low</option><option value="high">High</option>
</select>
</div>
<div class="form-row">
<label for="taskStatus">Column</label>
<select class="field" id="taskStatus">
<option value="todo">To do</option><option value="progress">In progress</option><option value="done">Done</option>
</select>
</div>
<div class="modal-actions">
<button type="button" class="button" id="cancelModal">Cancel</button>
<button type="submit" class="button primary">Add task</button>
</div>
</form>
</div>
<script>
(() => {
const statuses = [
{ id: "todo", title: "To do" },
{ id: "progress", title: "In progress" },
{ id: "done", title: "Done" }
];
let cards = [
{ id: 1, title: "Map out onboarding flow", priority: "high", status: "todo", owner: "AM", date: "Today" },
{ id: 2, title: "Write launch announcement", priority: "medium", status: "todo", owner: "JL", date: "Jun 18" },
{ id: 3, title: "Review customer interview notes", priority: "low", status: "todo", owner: "SK", date: "Jun 20" },
{ id: 4, title: "Build responsive pricing page", priority: "high", status: "progress", owner: "AM", date: "Jun 17" },
{ id: 5, title: "QA signup and checkout flow", priority: "medium", status: "progress", owner: "JL", date: "Jun 18" },
{ id: 6, title: "Finalize visual identity", priority: "low", status: "done", owner: "SK", date: "Done" }
];
const board = document.getElementById("board");
const search = document.getElementById("search");
const backdrop = document.getElementById("backdrop");
const titleInput = document.getElementById("taskTitle");
let draggedId = null;
let query = "";
const esc = s => String(s).replace(/[&<>"']/g, c => ({ "&":"&", "<":"<", ">":">", '"':""", "'":"'" }[c]));
function render() {
board.innerHTML = statuses.map(col => {
const visible = cards.filter(c => c.status === col.id && c.title.toLowerCase().includes(query));
const count = cards.filter(c => c.status === col.id).length;
return `<section class="column" data-status="${col.id}">
<div class="column-head">
<div class="col-label"><span class="dot"></span>${col.title}<span class="count">${count}</span></div>
<button class="add-inline" data-add="${col.id}" title="Add task to ${col.title}" aria-label="Add task to ${col.title}">+</button>
</div>
<div class="cards">${visible.length ? visible.map(c => `
<article class="card" draggable="true" data-id="${c.id}">
<div class="card-top">
<div class="card-title">${esc(c.title)}</div>
<button class="delete" data-delete="${c.id}" title="Delete task" aria-label="Delete ${esc(c.title)}">
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 7h16M10 11v6m4-6v6M5 7l1 14h12l1-14M9 7V4h6v3"></path></svg>
</button>
</div>
<div class="card-bottom">
<span class="priority ${c.priority}">${c.priority[0].toUpperCase()+c.priority.slice(1)}</span>
<span class="meta"><span class="mini-avatar">${esc(c.owner)}</span>${esc(c.date)}</span>
</div>
</article>`).join("") : `<div class="column-empty" style="display:block">${query ? "No matching tasks" : "Drop a task here"}</div>`}</div>
</section>`;
}).join("");
board.querySelectorAll(".card").forEach(card => {
card.addEventListener("dragstart", e => {
draggedId = Number(card.dataset.id);
card.classList.add("dragging");
e.dataTransfer.effectAllowed = "move";
e.dataTransfer.setData("text/plain", String(draggedId));
});
card.addEventListener("dragend", () => {
card.classList.remove("dragging");
board.querySelectorAll(".column").forEach(x => x.classList.remove("drag-over"));
draggedId = null;
});
});
board.querySelectorAll(".column").forEach(col => {
col.addEventListener("dragover", e => { e.preventDefault(); col.classList.add("drag-over"); });
col.addEventListener("dragleave", e => { if (!col.contains(e.relatedTarget)) col.classList.remove("drag-over"); });
col.addEventListener("drop", e => {
e.preventDefault(); col.classList.remove("drag-over");
const id = draggedId || Number(e.dataTransfer.getData("text/plain"));
const card = cards.find(c => c.id === id);
if (card) { card.status = col.dataset.status; render(); }
});
});
board.querySelectorAll("[data-delete]").forEach(btn => btn.addEventListener("click", () => {
cards = cards.filter(c => c.id !== Number(btn.dataset.delete)); render();
}));
board.querySelectorAll("[data-add]").forEach(btn => btn.addEventListener("click", () => openForm(btn.dataset.add)));
}
function openForm(status = "todo") {
document.getElementById("taskStatus").value = status;
backdrop.classList.add("open");
titleInput.value = "";
setTimeout(() => titleInput.focus(), 40);
}
function closeForm() { backdrop.classList.remove("open"); }
document.getElementById("openModal").addEventListener("click", () => openForm());
document.getElementById("closeModal").addEventListener("click", closeForm);
document.getElementById("cancelModal").addEventListener("click", closeForm);
backdrop.addEventListener("click", e => { if (e.target === backdrop) closeForm(); });
document.addEventListener("keydown", e => {
if (e.key === "Escape") closeForm();
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") { e.preventDefault(); search.focus(); }
});
document.getElementById("taskForm").addEventListener("submit", e => {
e.preventDefault();
const title = titleInput.value.trim();
if (!title) return;
cards.unshift({
id: Date.now(), title, priority: document.getElementById("taskPriority").value,
status: document.getElementById("taskStatus").value, owner: "AM", date: "Today"
});
closeForm(); render();
});
search.addEventListener("input", () => { query = search.value.trim().toLowerCase(); render(); });
document.getElementById("themeToggle").addEventListener("click", () => {
document.body.classList.toggle("dark");
document.getElementById("themeIcon").innerHTML = document.body.classList.contains("dark")
? '<circle cx="12" cy="12" r="4"></circle><path d="M12 2v2m0 16v2M4.93 4.93l1.42 1.42m11.3 11.3 1.42 1.42M2 12h2m16 0h2M4.93 19.07l1.42-1.42m11.3-11.3 1.42-1.42"></path>'
: '<path d="M20.1 15.4A8.5 8.5 0 0 1 8.6 3.9 8.5 8.5 0 1 0 20.1 15.4Z"></path>';
});
render();
})();
</script>
</body>
</html>Reasoning trace (63 tokens)
**Planning UI features** I need to produce a polished full HTML, focusing on drag-and-drop functionality, modals, search options, and theming. I want the layout to be clear, using screenshots for reference. The app UI should be crisp and mobile-friendly, possibly with a toolbar. I’ll incorporate inline icons, either through Unicode or SVG. Card actions will include delete options, and I’m considering a three-column layout along with an empty state and a drag indicator. I’ll rely on JS and CSS for this.