Models 44

GPT-5 Nano

by OpenAIReasoningClean
Open in new window

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>