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
36.9 s
Cost
$0.0021
Output tokens
5,211
Spent thinking
384 tokens
Speed
150.2 tok/s
Console errors
0
First token after
2.2 s
First visible text after
7.4 s
Prompt tokens
493
Code size
17.7 KB
Finish reason
stop
Served by
OpenAI
Model id
openai/gpt-5-nano
Reasoning sent
{"effort":"low"}
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 · 589 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Vanilla Kanban Board</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <style>
    :root {
      --bg: #f5f6f8;
      --surface: #ffffff;
      --text: #1f2937;
      --muted: #64748b;
      --card: #ffffff;
      --shadow: 0 4px 14px rgba(0,0,0,.08);
      --accent: #4f46e5;
      --low: #10b981;
      --med: #f59e0b;
      --high: #ef4444;
      --border: #e5e7eb;
      --overlay: rgba(0,0,0,.25);
      --chip-bg: #eef2ff;
      --chip-text: #3730a3;
      --bg-soft: #f0f2f5;
    }
    .dark {
      --bg: #0b1020;
      --surface: #141a2b;
      --text: #e2e8f0;
      --muted: #93a1b5;
      --card: #1e2540;
      --shadow: 0 6px 20px rgba(0,0,0,.5);
      --accent: #8b5cf6;
      --low: #10b981;
      --med: #f59e0b;
      --high: #f87171;
      --border: #2a3153;
      --overlay: rgba(0,0,0,.6);
      --chip-bg: #334155;
      --chip-text: #e2e8f0;
      --bg-soft: #0b1220;
    }

    * { box-sizing: border-box; }
    html, body { height: 100%; }
    body {
      margin: 0;
      font-family: Inter, system-ui, -apple-system, "Segoe UI", Roboto;
      background: var(--bg);
      color: var(--text);
      transition: background 0.25s ease, color 0.25s ease;
      overflow: hidden;
    }

    /* Layout */
    .app {
      height: 100%;
      display: grid;
      grid-template-rows: auto 1fr;
    }
    header {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: 14px 20px;
      border-bottom: 1px solid var(--border);
      background: linear-gradient(to right, rgba(0,0,0,.0), rgba(0,0,0,.02));
      position: sticky; top: 0; z-index: 10;
    }
    .title {
      display: flex; align-items: center; gap: 12px;
      font-weight: 700; letter-spacing: .2px;
    }
    .title .logo {
      width: 28px; height: 28px; border-radius: 6px;
      background: linear-gradient(135deg, var(--accent), #60a5fa);
      display: inline-block;
    }
    .controls { display: flex; align-items: center; gap: 10px; }

    .title small { color: var(--muted); font-weight: 500; }

    .btn {
      border: 1px solid var(--border);
      background: var(--surface);
      color: var(--text);
      padding: 8px 12px; border-radius: 8px;
      cursor: pointer;
      font-weight: 600;
    }
    .btn.primary {
      background: var(--accent);
      color: white;
      border: none;
    }
    .btn.icon {
      width: 38px; height: 38px; display: inline-flex;
      align-items: center; justify-content: center;
    }

    /* Board area */
    .board {
      padding: 14px;
      height: calc(100% - 0px);
      overflow: hidden;
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
      gap: 14px;
    }
    .column {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 12px;
      display: flex; flex-direction: column;
      min-width: 240px;
      max-width: 100%;
      height: 100%;
      box-shadow: var(--shadow);
      overflow: hidden;
    }
    .column-header {
      display: flex; align-items: center; justify-content: space-between;
      padding: 6px 6px 8px;
      border-bottom: 1px dashed var(--border);
      margin-bottom: 8px;
    }
    .column-title { font-weight: 700; font-size: 14px; letter-spacing: .4px; }
    .count { font-size: 12px; color: var(--muted); padding: 2px 8px; border-radius: 999px; background: #eef2ff; }

    .dropzone {
      min-height: 60px;
      padding: 6px;
      flex: 1;
      display: flex; flex-direction: column;
      gap: 8px;
      align-items: stretch;
    }
    .drop-target {
      outline: 2px dashed transparent;
      border-radius: 8px;
      padding: 6px;
      min-height: 8px;
      transition: outline-color .2s ease;
    }
    .drop-target.active {
      outline-color: var(--accent);
    }

    .card {
      padding: 10px 12px;
      border-radius: 10px;
      background: var(--card);
      border: 1px solid var(--border);
      box-shadow: 0 2px 6px rgba(0,0,0,.05);
      display: flex; flex-direction: column; gap: 6px;
      cursor: grab;
    }
    .card:active { cursor: grabbing; }
    .card-title { font-weight: 700; font-size: 14px; }
    .card-meta { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
    .badge {
      font-size: 11px; font-weight: 700;
      padding: 4px 8px; border-radius: 999px;
      display: inline-flex; align-items: center; gap: 6px;
      background: var(--chip-bg); color: var(--chip-text);
      text-transform: uppercase;
    }
    .badge.low { background: rgba(16,185,129,.15); color: #14532d; }
    .badge.med { background: rgba(245,158,11,.15); color: #6b4c00; }
    .badge.high { background: rgba(239,68,68,.15); color: #6b1b1b; }

    .card-actions { display: flex; gap: 6px; justify-content: flex-end; }

    /* modal */
    .modal-backdrop {
      position: fixed; inset: 0;
      background: var(--overlay);
      display: flex; align-items: center; justify-content: center;
      z-index: 20;
    }
    .modal {
      width: min(420px, 90vw);
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 12px;
      padding: 16px;
      box-shadow: 0 20px 60px rgba(0,0,0,.25);
    }
    .modal h3 { margin: 0 0 12px; }
    .form-row { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
    .form-row label { font-size: 12px; color: var(--muted); }
    .form-row input, .form-row select {
      padding: 10px 12px; border-radius: 8px;
      border: 1px solid var(--border);
      background: var(--surface); color: var(--text);
    }
    .modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 8px; }

    /* search bar */
    .searchbar {
      display: flex; align-items: center; gap: 8px;
      padding: 8px 12px; border-radius: 999px; border: 1px solid var(--border);
      background: var(--surface);
    }
    .searchbar input {
      border: none; outline: none; background: transparent;
      padding: 6px; width: 180px;
    }

    /* responsive */
    @media (max-width: 1020px) {
      .board { grid-template-columns: 1fr 1fr; height: auto; }
    }
    @media (max-width: 720px) {
      .board { grid-template-columns: 1fr; height: auto; }
      .column { min-width: auto; }
    }
  </style>
</head>
<body>
  <div class="app">
    <header>
      <div class="title" aria-label="Kanban board">
        <span class="logo" aria-hidden="true"></span>
        <div>
          <div>Kanban Board</div>
          <small>Vanilla JS, no frameworks</small>
        </div>
      </div>

      <div class="controls" aria-label="Controls">
        <div class="searchbar" role="search" aria-label="Search cards">
          <span>🔎</span>
          <input id="search" type="search" placeholder="Search by title…" autocomplete="off" />
        </div>
        <button id="themeToggle" class="btn" aria-label="Toggle theme">Dark</button>
        <button id="openModal" class="btn primary" aria-label="Add card">New card</button>
      </div>
    </header>

    <main class="board" id="board" aria-label="Kanban board">
      <!-- Columns are generated by JS -->
    </main>
  </div>

  <!-- modal (hidden by default) -->
  <div id="modalHost" style="display:none"></div>

  <script>
    // State (in memory only)
    const state = {
      // columns hold arrays of cards (id, title, priority)
      columns: {
        todo: [],
        inprogress: [],
        done: []
      },
      // for dnd
      draggingId: null,
      // theme
      theme: 'light'
    };

    // Helpers
    const priColor = (p) => {
      if (p === 'low') return 'low';
      if (p === 'medium') return 'med';
      return 'high';
    };

    // Prepopulate 6 cards across columns
    function seed() {
      state.columns.todo = [
        { id: 'c1', title: 'Set up project structure', priority: 'low' },
        { id: 'c2', title: 'Define components and data models', priority: 'high' }
      ];
      state.columns.inprogress = [
        { id: 'c3', title: 'Implement drag and drop', priority: 'medium' },
      ];
      state.columns.done = [
        { id: 'c4', title: 'Create initial UI wireframes', priority: 'low' },
        { id: 'c5', title: 'Configure build tooling', priority: 'medium' },
        { id: 'c6', title: 'Write project README', priority: 'low' }
      ];
    }

    // Rendering
    const boardEl = document.getElementById('board');
    const searchEl = document.getElementById('search');
    const themeBtn = document.getElementById('themeToggle');
    const modalRoot = document.getElementById('modalHost');
    let searchTerm = '';

    function render() {
      // clear
      boardEl.innerHTML = '';
      // Build columns
      const cols = [
        { key: 'todo', title: 'To do' },
        { key: 'inprogress', title: 'In progress' },
        { key: 'done', title: 'Done' }
      ];
      cols.forEach(col => {
        const container = document.createElement('section');
        container.className = 'column';
        container.dataset.col = col.key;

        // header
        const header = document.createElement('div');
        header.className = 'column-header';
        const hTitle = document.createElement('div');
        hTitle.className = 'column-title';
        hTitle.textContent = col.title;
        const count = document.createElement('span');
        count.className = 'count';
        const items = state.columns[col.key] || [];
        // live search filter
        const visibleItems = items.filter(card => {
          if (!searchTerm) return true;
          return card.title.toLowerCase().includes(searchTerm.toLowerCase());
        });
        count.textContent = visibleItems.length;

        header.appendChild(hTitle);
        header.appendChild(count);
        container.appendChild(header);

        const dropzone = document.createElement('div');
        dropzone.className = 'dropzone';
        // real drop target
        dropzone.dataset.col = col.key;

        // render cards
        visibleItems.forEach(card => {
          const c = document.createElement('div');
          c.className = 'card';
          c.draggable = true;
          c.dataset.id = card.id;

          const t = document.createElement('div');
          t.className = 'card-title';
          t.textContent = card.title;

          const meta = document.createElement('div');
          meta.className = 'card-meta';

          const badge = document.createElement('span');
          badge.className = 'badge ' + priColor(card.priority);
          badge.textContent = card.priority;

          const actions = document.createElement('div');
          actions.className = 'card-actions';
          const del = document.createElement('button');
          del.className = 'btn';
          del.textContent = 'Delete';
          del.style.fontSize = '11px';
          del.style.padding = '6px 8px';
          del.addEventListener('click', (e) => {
            e.stopPropagation();
            deleteCard(card.id);
          });

          actions.appendChild(del);
          meta.appendChild(badge);
          meta.appendChild(actions);

          c.appendChild(t);
          c.appendChild(meta);

          // DnD events
          c.addEventListener('dragstart', (e) => {
            state.draggingId = card.id;
            e.dataTransfer.setData('text/plain', card.id);
            e.dataTransfer.effectAllowed = 'move';
          });
          c.addEventListener('dragend', () => {
            state.draggingId = null;
            updateDropIndicators();
          });

          dropzone.appendChild(c);
        });

        // dropzone area - enable drop
        dropzone.addEventListener('dragover', (e) => {
          e.preventDefault();
          e.dataTransfer.dropEffect = 'move';
          setActiveDrop(dropzone);
        });
        dropzone.addEventListener('dragleave', () => {
          clearActiveDrop(dropzone);
        });
        dropzone.addEventListener('drop', (e) => {
          e.preventDefault();
          const id = e.dataTransfer.getData('text/plain');
          moveCardTo(id, col.key);
          clearActiveDrop(dropzone);
        });

        container.appendChild(dropzone);
        boardEl.appendChild(container);
      });
    }

    function setActiveDrop(el) {
      el.classList.add('active');
    }
    function clearActiveDrop(el) {
      el.classList.remove('active');
    }
    function updateDropIndicators() {
      // remove active on all
      document.querySelectorAll('.dropzone').forEach(d => d.classList.remove('active'));
    }

    function moveCardTo(cardId, targetCol) {
      // find and move
      let moved = false;
      for (const [key, arr] of Object.entries(state.columns)) {
        const idx = arr.findIndex(c => c.id === cardId);
        if (idx >= 0) {
          const [card] = state.columns[key].splice(idx, 1);
          state.columns[targetCol].unshift(card); // put at top for visibility
          moved = true;
          break;
        }
      }
      if (moved) render();
    }

    function deleteCard(cardId) {
      for (const key of Object.keys(state.columns)) {
        const arr = state.columns[key];
        const idx = arr.findIndex(c => c.id === cardId);
        if (idx >= 0) {
          arr.splice(idx, 1);
          render();
          break;
        }
      }
    }

    // Modal for adding a card
    function openModal() {
      // create modal
      const modal = document.createElement('div');
      modal.className = 'modal-backdrop';
      modal.innerHTML = `
        <div class="modal" role="dialog" aria-label="Add card">
          <h3>New card</h3>
          <div class="form">
            <div class="form-row">
              <label for="cardTitle">Title</label>
              <input id="cardTitle" type="text" placeholder="e.g., Write tests" />
            </div>
            <div class="form-row">
              <label for="cardPriority">Priority</label>
              <select id="cardPriority">
                <option value="low">Low</option>
                <option value="medium" selected>Medium</option>
                <option value="high">High</option>
              </select>
            </div>
          </div>
          <div class="modal-actions">
            <button class="btn" id="cancelBtn" aria-label="Cancel">Cancel</button>
            <button class="btn primary" id="addBtn" aria-label="Add">Add card</button>
          </div>
        </div>
      `;
      modalHost.style.display = 'block';
      modalHost.appendChild(modal);

      const titleInput = modal.querySelector('#cardTitle');
      const prioritySel = modal.querySelector('#cardPriority');
      const addBtn = modal.querySelector('#addBtn');
      const cancelBtn = modal.querySelector('#cancelBtn');

      // focus title for quick adding
      setTimeout(() => titleInput.focus(), 0);

      function close() {
        modalHost.removeChild(modal);
        modalHost.style.display = 'none';
      }

      addBtn.addEventListener('click', () => {
        const t = titleInput.value.trim();
        if (!t) {
          titleInput.focus();
          return;
        }
        // default to To Do if user not specifying column; place in To Do
        const newCard = {
          id: 'c' + Math.random().toString(36).slice(2, 7),
          title: t,
          priority: prioritySel.value
        };
        state.columns.todo.unshift(newCard);
        render();
        close();
      });

      cancelBtn.addEventListener('click', close);

      // close on backdrop click
      modal.addEventListener('mousedown', (e) => {
        if (e.target === modal) close();
      });
    }

    // Theme toggle
    function applyTheme() {
      if (state.theme === 'dark') {
        document.body.classList.add('dark');
        themeBtn.textContent = 'Light';
      } else {
        document.body.classList.remove('dark');
        themeBtn.textContent = 'Dark';
      }
    }

    // Search live filter
    searchEl.addEventListener('input', (e) => {
      searchTerm = e.target.value.trim();
      render();
    });

    // Start message behavior: require click to start because of iframe keyboard rule
    // We'll show a lightweight hint overlay on first paint and hide on click or keydown
    function showStartHint() {
      const hint = document.createElement('div');
      hint.id = 'startHint';
      hint.style.position = 'fixed';
      hint.style.left = '50%';
      hint.style.top = '50%';
      hint.style.transform = 'translate(-50%, -50%)';
      hint.style.background = 'var(--surface)';
      hint.style.border = '1px solid var(--border)';
      hint.style.borderRadius = '12px';
      hint.style.padding = '16px 20px';
      hint.style.boxShadow = '0 10px 40px rgba(0,0,0,.25)';
      hint.style.zIndex = '999';
      hint.innerHTML = `
        <strong>Click or press any key</strong> to start
        <div style="font-size:12px; color:var(--muted); margin-top:6px;">
          Drag cards between columns after you start.
        </div>
      `;
      document.body.appendChild(hint);

      function start() {
        document.body.removeChild(hint);
        window.removeEventListener('mousedown', start);
        window.removeEventListener('keydown', start);
      }

      window.addEventListener('mousedown', start, { once: true });
      window.addEventListener('keydown', start, { once: true });
    }

    // Init
    function init() {
      seed();
      render();
      applyTheme();

      // event bindings
      document.addEventListener('dragend', () => updateDropIndicators());
      document.addEventListener('dragover', (e) => {
        // allow dropping anywhere if dragging a card near a column
        e.preventDefault();
      });

      document.getElementById('openModal').addEventListener('click', openModal);
      themeBtn.addEventListener('click', () => {
        state.theme = (state.theme === 'light') ? 'dark' : 'light';
        applyTheme();
      });

      // Start hint
      showStartHint();
    }

    // Kick off
    init();
  </script>
</body>
</html>
Reasoning trace (384 tokens)
**Implementing Kanban functionality**

I need to create a single HTML block with embedded CSS and JS for a Kanban board with features. The self-playing demo isn't strictly necessary since it’s more for games, but I wonder if it could be useful here as a demo. I think an initial message like “Click or press a key to start” is required for the keyboard option. I should ensure that our overlay text appears on first interaction to enable drag and drop interactions.**Creating Kanban board features**

I’m planning to implement a Kanban board with three columns, each showing card counts. Each card will have a title and a priority badge color, and I’ll start by prepopulating six cards across the columns. I’ll add a modal form for cards requiring a title and a priority selection, and a delete button for each card.

For drag and drop, I’ll handle events like dragstart and drop, with visual indicators for highlighting columns. I’ll also include a live search input for filtering cards by title and a theme toggle for light and dark modes, stored in a memory variable. The board's state will be represented as arrays of cards without storage.**Enhancing drag and drop functionality**

I need to ensure that during the dragover event, I prevent the default behavior and show a drop indicator by adding a class to the column. Upon dropping, the card should move to the target column, and I'll update the data and re-render the board.

There’s also a mention of an automatic screenshot after three seconds, but since that’s not necessary here, I’ll focus on ensuring the first paint shows all elements promptly. Also, I’ll include CSS for responsive design. Let's go ahead and craft the code!