GPT-5 Nano
by OpenAIReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 30.0 s
- Cost
- $0.0018
- Output tokens
- 4,439
- Spent thinking
- 0 tokens
- Speed
- 152.8 tok/s
- Console errors
- 0
- First token after
- 959 ms
- First visible text after
- 959 ms
- Prompt tokens
- 493
- Code size
- 15.5 KB
- Finish reason
- stop
- Served by
- OpenAI
- Model id
openai/gpt-5-nano- Reasoning sent
{"effort":"minimal"}- 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 · 562 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Vanilla Kanban Board</title>
<style>
:root {
--bg: #0f1117;
--surface: #141824;
--card: #1e2030;
--text: #e7e9f3;
--muted: #a2a8b8;
--accent: #7c5cff;
--green: #36d399;
--blue: #4dabf7;
--red: #f87171;
--shadow: 0 6px 20px rgba(0,0,0,.25);
--column: #1d2030;
--border: rgba(255,255,255,.08);
--chip-low: #93c5fd;
--chip-med: #f59e0b;
--chip-high: #f87171;
}
[data-theme="light"] {
--bg: #f5f7fb;
--surface: #ffffff;
--card: #ffffff;
--text: #0f1220;
--muted: #5b6470;
--accent: #4e6ef2;
--shadow: 0 6px 20px rgba(0,0,0,.08);
--column: #f1f5f9;
--border: rgba(15,23,42,.08);
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto, Arial;
color: var(--text);
background: radial-gradient(circle at 20% -10%, rgba(124,92,255,.15), transparent 40%),
radial-gradient(circle at 100% 0%, rgba(0,150,255,.12), transparent 40%),
var(--bg);
display: flex;
align-items: stretch;
justify-content: center;
padding: 20px;
}
.board {
width: min(1100px, 100%);
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 16px;
}
.column {
background: linear-gradient(#1b2030, #151a2a);
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px;
min-height: 420px;
display: flex;
flex-direction: column;
box-shadow: var(--shadow);
}
.column-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 8px 12px;
border-bottom: 1px dashed var(--border);
margin-bottom: 8px;
}
.column-title {
font-weight: 700;
letter-spacing: .4px;
}
.badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 8px;
border-radius: 999px;
font-size: 12px;
font-weight: 600;
color: white;
background: gray;
}
.badge.low { background: var(--chip-low); }
.badge.medium { background: var(--chip-med); }
.badge.high { background: var(--chip-high); }
.card {
background: var(--card);
border: 1px solid var(--border);
border-radius: 10px;
padding: 10px 10px 8px;
margin: 8px 4px;
cursor: grab;
user-select: none;
}
.card.dragging { opacity: .5; transform: scale(.98); }
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 8px;
}
.card-title {
font-weight: 700;
font-size: 14px;
line-height: 1.2;
margin: 2px 0 6px;
}
.card-actions {
display: flex;
gap: 6px;
}
.btn {
border: none;
border-radius: 6px;
padding: 6px 10px;
font-size: 12px;
cursor: pointer;
color: white;
background: #374151;
}
.btn.delete { background: #e11d48; }
.btn.add { background: var(--accent); }
.btn.primary { background: #1e88e5; }
.tooling {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 12px;
padding: 0 4px;
}
.search {
flex: 1;
min-width: 180px;
display: flex;
align-items: center;
gap: 8px;
padding: 8px 10px;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--surface);
}
.search input {
border: none;
outline: none;
width: 100%;
background: transparent;
color: var(--text);
}
.toggle {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 8px;
border: 1px solid var(--border);
background: var(--surface);
cursor: pointer;
}
.drop-indicator {
height: 6px;
width: 100%;
margin: 6px 0 0;
border-radius: 6px;
background: linear-gradient(90deg, transparent, rgba(255,255,255,.8), transparent);
opacity: 0;
transform: scaleY(.5);
transition: opacity .15s;
}
.drop-target {
outline: 2px dashed rgba(124,92,255,.8);
outline-offset: -6px;
border-radius: 8px;
}
.hidden { display: none; }
@media (max-width: 900px) {
.board { grid-template-columns: 1fr; }
}
/* Start overlay message */
.start-overlay {
position: fixed;
inset: 0;
display: grid;
place-items: center;
background: rgba(0,0,0,.45);
z-index: 50;
text-align: center;
padding: 20px;
}
.start-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px;
max-width: 420px;
box-shadow: var(--shadow);
}
.start-card h2 { margin: 0 0 12px; }
.start-card p { color: var(--muted); margin: 0 0 12px; }
.start-card .hint { font-size: 12px; color: var(--muted); }
.hidden-focusable { outline: none; }
/* Modal for new card */
.modal {
position: fixed;
inset: 0;
display: grid;
place-items: center;
background: rgba(0,0,0,.45);
z-index: 60;
}
.modal-panel {
width: min(420px, 90vw);
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 16px;
box-shadow: var(--shadow);
}
.modal-panel h3 { margin-top: 0; }
.form-row { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }
.form-row label { font-size: 12px; color: var(--muted); }
.form-row input, .form-row select, .form-row textarea {
padding: 8px 10px;
border-radius: 6px;
border: 1px solid var(--border);
background: #11131a;
color: #fff;
}
.form-row input:focus, .form-row select:focus, .form-row textarea:focus {
outline: 2px solid var(--accent);
}
.modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 6px; }
</style>
</head>
<body>
<div class="start-overlay" id="startOverlay" aria-label="Start screen">
<div class="start-card">
<h2>Kanban Board</h2>
<p>A live, self-contained Kanban demo in vanilla JS.</p>
<p class="hint">Click anywhere or press a key to start the demo.</p>
</div>
</div>
<div style="width:100%;max-width:1200px; margin:0 auto; padding:0 8px;">
<div class="toolbar tooling" aria-label="Board tools" style="justify-content: space-between; align-items: center; padding: 6px 0 2px;">
<div class="search" role="search">
<span>🔎</span>
<input id="search" placeholder="Search cards by title..." aria-label="Search cards" />
</div>
<div style="display:flex; gap:8px; align-items:center;">
<button class="btn add" id="newCardBtn" aria-label="Add card">+ Add Card</button>
<button class="toggle" id="themeToggle" aria-label="Toggle theme">🌞/🌜 Theme</button>
</div>
</div>
<section class="board" id="board" aria-label="Kanban board with three columns">
<!-- Columns will be populated by JS -->
</section>
</div>
<!-- Hidden modal for adding card -->
<div class="modal hidden" id="modal" aria-hidden="true" aria-label="Add card modal">
<div class="modal-panel">
<h3>New Card</h3>
<div class="form-row">
<label for="title">Title</label>
<input id="title" type="text" placeholder="e.g., Implement drag-and-drop" />
</div>
<div class="form-row">
<label for="priority">Priority</label>
<select id="priority">
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
</select>
</div>
<div class="form-row">
<label for="description" style="font-size:12px; color:var(--muted);">Description (optional)</label>
<textarea id="description" rows="3" placeholder="A short description..."></textarea>
</div>
<div class="modal-actions">
<button class="btn" id="cancelBtn" aria-label="Cancel">Cancel</button>
<button class="btn primary" id="saveBtn" aria-label="Save">Save</button>
</div>
</div>
</div>
<script>
// In-memory state
const state = {
cards: [
// To do
{ id: 'c1', title: 'Create project setup', priority: 'low', column: 'todo' },
// In progress
{ id: 'c2', title: 'Design Kanban board UI', priority: 'high', column: 'inprogress' },
// In progress
{ id: 'c3', title: 'Implement drag-and-drop', priority: 'high', column: 'inprogress' },
// Done
{ id: 'c4', title: 'Define MVP scope', priority: 'medium', column: 'done' },
// To do
{ id: 'c5', title: 'Write tests for drag logic', priority: 'low', column: 'todo' },
// Done
{ id: 'c6', title: 'Initialize Git repo', priority: 'low', column: 'done' },
],
theme: 'dark',
};
const COLUMNS = [
{ key: 'todo', title: 'To do' },
{ key: 'inprogress', title: 'In progress' },
{ key: 'done', title: 'Done' },
];
const boardEl = document.getElementById('board');
const startOverlay = document.getElementById('startOverlay');
const searchEl = document.getElementById('search');
const modal = document.getElementById('modal');
const newCardBtn = document.getElementById('newCardBtn');
const saveBtn = document.getElementById('saveBtn');
const cancelBtn = document.getElementById('cancelBtn');
const titleInput = document.getElementById('title');
const prioritySelect = document.getElementById('priority');
const descriptionInput = document.getElementById('description');
const themeToggle = document.getElementById('themeToggle');
// Initialize theme
function applyTheme() {
document.documentElement.setAttribute('data-theme', state.theme === 'dark' ? 'dark' : 'light');
}
// Build board UI
function renderBoard() {
boardEl.innerHTML = '';
const filter = (searchEl.value || '').toLowerCase();
COLUMNS.forEach(col => {
// Column container
const colDiv = document.createElement('section');
colDiv.className = 'column';
colDiv.dataset.column = col.key;
// Column header
const header = document.createElement('div');
header.className = 'column-header';
const title = document.createElement('div');
title.className = 'column-title';
title.textContent = col.title;
// Card count badge
const count = document.createElement('span');
count.className = 'badge';
const cardCount = state.cards.filter(c => c.column === col.key && c.title.toLowerCase().includes(filter)).length;
count.textContent = `${cardCount}`;
header.appendChild(title);
header.appendChild(count);
colDiv.appendChild(header);
// Drop area
colDiv.addEventListener('dragover', (e) => {
e.preventDefault();
// add visual
colDiv.classList.add('drop-target');
});
colDiv.addEventListener('dragleave', () => {
colDiv.classList.remove('drop-target');
});
// For drop
colDiv.addEventListener('drop', (e) => {
e.preventDefault();
colDiv.classList.remove('drop-target');
const cardId = e.dataTransfer.getData('text/card-id');
if (cardId) {
const card = state.cards.find(c => c.id === cardId);
if (card && card.column !== col.key) {
card.column = col.key;
renderBoard();
}
}
});
// Cards in this column
const cardsInCol = state.cards.filter(c => c.column === col.key && c.title.toLowerCase().includes(filter));
cardsInCol.forEach(card => {
const cardEl = document.createElement('div');
cardEl.className = 'card';
cardEl.draggable = true;
cardEl.dataset.cardId = card.id;
// Drag events
cardEl.addEventListener('dragstart', (ev) => {
ev.dataTransfer.setData('text/card-id', card.id);
cardEl.classList.add('dragging');
// indicate drop areas
document.querySelectorAll('.column').forEach(colNode => {
colNode.classList.add('drop-target');
});
});
cardEl.addEventListener('dragend', () => {
cardEl.classList.remove('dragging');
document.querySelectorAll('.column').forEach(colNode => {
colNode.classList.remove('drop-target');
});
});
// Card content
const headerRow = document.createElement('div');
headerRow.className = 'card-header';
const titleEl = document.createElement('div');
titleEl.className = 'card-title';
titleEl.textContent = card.title;
const actions = document.createElement('div');
actions.className = 'card-actions';
const delBtn = document.createElement('button');
delBtn.className = 'btn delete';
delBtn.textContent = 'Delete';
delBtn.addEventListener('click', (e) => {
e.stopPropagation();
deleteCard(card.id);
});
actions.appendChild(delBtn);
headerRow.appendChild(titleEl);
headerRow.appendChild(actions);
const priorityBadge = document.createElement('span');
priorityBadge.className = `badge ${card.priority}`;
priorityBadge.textContent = card.priority.charAt(0).toUpperCase() + card.priority.slice(1);
cardEl.appendChild(headerRow);
cardEl.appendChild(priorityBadge);
// Append to column
colDiv.appendChild(cardEl);
});
boardEl.appendChild(colDiv);
});
}
function deleteCard(id) {
const idx = state.cards.findIndex(c => c.id === id);
if (idx >= 0) {
state.cards.splice(idx, 1);
renderBoard();
}
}
// Start overlay dismissal
function startDemo() {
startOverlay.style.display = 'none';
// Focus search for accessibility (optional)
searchEl.focus();
}
// Add card modal logic
function openModal() {
modal.classList.remove('hidden');
modal.setAttribute('aria-hidden', 'false');
// Reset fields
titleInput.value = '';
descriptionInput.value = '';
prioritySelect.value = 'medium';
titleInput.focus();
}
function closeModal() {
modal.classList.add('hidden');
modal.setAttribute('aria-hidden', 'true');
}
function saveCard() {
const t = titleInput.value.trim();
if (!t) {
titleInput.focus();
return;
}
const newCard = {
id: 'c' + Math.random().toString(36).slice(2, 7),
title: t,
priority: prioritySelect.value,
column: 'todo'
};
state.cards.push(newCard);
closeModal();
renderBoard();
}
// Drag-and-drop area guidance: also allow dropping into empty space by cards with drop on column
// Initialize sample UI
applyTheme();
renderBoard();
// Event bindings
startOverlay.addEventListener('click', startDemo);
document.addEventListener('keydown', (e) => {
if (startOverlay.style.display !== 'none' && (e.key.length === 1 || e.key === 'Escape')) {
startDemo();
}
}, { once: false });
newCardBtn.addEventListener('click', openModal);
saveBtn.addEventListener('click', saveCard);
cancelBtn.addEventListener('click', closeModal);
// Theme toggle cycles
themeToggle.addEventListener('click', () => {
state.theme = state.theme === 'dark' ? 'light' : 'dark';
applyTheme();
});
// Live search
searchEl.addEventListener('input', () => {
renderBoard();
});
// Accessibility: allow Enter to save in modal
titleInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter') saveCard();
});
// Ensure first paint looks alive: show initial counts after a short delay (screenshot-friendly)
window.addEventListener('load', () => {
// small delay to ensure DOM ready
setTimeout(() => {
renderBoard();
}, 60);
});
</script>
</body>
</html>