Models 44

Claude Haiku 4.5

by AnthropicReasoningClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
44.5 s
Cost
$0.085
Output tokens
16,959
Spent thinking
11,715 tokens
Speed
389.2 tok/s
Console errors
0
First token after
897 ms
First visible text after
39.4 s
Prompt tokens
553
Code size
18.5 KB
Finish reason
stop
Served by
Amazon Bedrock
Model id
anthropic/claude-haiku-4.5
Reasoning sent
{"enabled":true}
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 · 638 lines

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kanban Board</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        :root {
            --bg-primary: #ffffff;
            --bg-secondary: #f5f5f5;
            --bg-tertiary: #e9ecef;
            --text-primary: #212529;
            --text-secondary: #6c757d;
            --border-color: #dee2e6;
            --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
            --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.1);
        }

        body.dark-theme {
            --bg-primary: #1e1e1e;
            --bg-secondary: #2d2d2d;
            --bg-tertiary: #3a3a3a;
            --text-primary: #e0e0e0;
            --text-secondary: #9e9e9e;
            --border-color: #404040;
            --shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
            --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.3);
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background-color: var(--bg-primary);
            color: var(--text-primary);
            transition: background-color 0.3s, color 0.3s;
        }

        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 20px;
            background-color: var(--bg-secondary);
            border-bottom: 1px solid var(--border-color);
            flex-wrap: wrap;
            gap: 15px;
        }

        .header h1 {
            font-size: 24px;
        }

        .header-controls {
            display: flex;
            gap: 15px;
            align-items: center;
            flex-wrap: wrap;
        }

        .search-box {
            padding: 8px 12px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            background-color: var(--bg-primary);
            color: var(--text-primary);
            font-size: 14px;
            min-width: 200px;
        }

        .search-box::placeholder {
            color: var(--text-secondary);
        }

        .btn {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            background-color: #007bff;
            color: white;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.2s;
        }

        .btn:hover:not(:disabled) {
            background-color: #0056b3;
        }

        .btn:disabled {
            opacity: 0.5;
            cursor: not-allowed;
        }

        .btn-small {
            padding: 4px 8px;
            font-size: 12px;
        }

        .theme-toggle {
            padding: 8px 16px;
            border: 1px solid var(--border-color);
            background-color: var(--bg-primary);
            color: var(--text-primary);
            border-radius: 4px;
            cursor: pointer;
            transition: all 0.2s;
        }

        .theme-toggle:hover {
            background-color: var(--bg-tertiary);
        }

        .container {
            display: flex;
            gap: 20px;
            padding: 20px;
            overflow-x: auto;
            min-height: calc(100vh - 100px);
        }

        .column {
            flex: 0 0 350px;
            background-color: var(--bg-secondary);
            border-radius: 8px;
            padding: 15px;
            display: flex;
            flex-direction: column;
            box-shadow: var(--shadow);
        }

        .column-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 2px solid var(--border-color);
        }

        .column-title {
            font-weight: 600;
            font-size: 16px;
        }

        .column-count {
            background-color: var(--text-secondary);
            color: var(--bg-primary);
            border-radius: 50%;
            width: 24px;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            font-weight: bold;
        }

        .cards-container {
            flex: 1;
            overflow-y: auto;
            padding-right: 5px;
            min-height: 100px;
            transition: background-color 0.2s;
        }

        .cards-container::-webkit-scrollbar {
            width: 6px;
        }

        .cards-container::-webkit-scrollbar-track {
            background: transparent;
        }

        .cards-container::-webkit-scrollbar-thumb {
            background: var(--border-color);
            border-radius: 3px;
        }

        .card {
            background-color: var(--bg-primary);
            border: 1px solid var(--border-color);
            border-radius: 6px;
            padding: 12px;
            margin-bottom: 12px;
            cursor: move;
            transition: all 0.2s;
            box-shadow: var(--shadow);
        }

        .card:hover {
            box-shadow: var(--shadow-lg);
            transform: translateY(-2px);
        }

        .card.dragging {
            opacity: 0.5;
        }

        .card-title {
            font-weight: 500;
            margin-bottom: 8px;
            word-break: break-word;
        }

        .card-footer {
            display: flex;
            justify-content: space-between;
            align-items: center;
            gap: 8px;
        }

        .priority-badge {
            padding: 3px 8px;
            border-radius: 3px;
            font-size: 11px;
            font-weight: 600;
            text-transform: uppercase;
            flex-shrink: 0;
        }

        .priority-low {
            background-color: #d4edda;
            color: #155724;
        }

        body.dark-theme .priority-low {
            background-color: #1b5e20;
            color: #81c784;
        }

        .priority-medium {
            background-color: #fff3cd;
            color: #856404;
        }

        body.dark-theme .priority-medium {
            background-color: #7d6608;
            color: #ffd54f;
        }

        .priority-high {
            background-color: #f8d7da;
            color: #721c24;
        }

        body.dark-theme .priority-high {
            background-color: #b71c1c;
            color: #ef5350;
        }

        .card-delete {
            padding: 4px 8px;
            background-color: #dc3545;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            font-size: 12px;
            transition: background-color 0.2s;
            flex-shrink: 0;
        }

        .card-delete:hover {
            background-color: #c82333;
        }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0, 0, 0, 0.5);
            z-index: 1000;
            align-items: center;
            justify-content: center;
        }

        .modal.show {
            display: flex;
        }

        .modal-content {
            background-color: var(--bg-primary);
            border-radius: 8px;
            padding: 30px;
            max-width: 400px;
            width: 90%;
            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
        }

        .modal-header {
            margin-bottom: 20px;
        }

        .modal-header h2 {
            font-size: 20px;
        }

        .modal-body {
            margin-bottom: 20px;
        }

        .form-group {
            margin-bottom: 15px;
        }

        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: 500;
            font-size: 14px;
        }

        .form-group input,
        .form-group select {
            width: 100%;
            padding: 8px 12px;
            border: 1px solid var(--border-color);
            border-radius: 4px;
            background-color: var(--bg-primary);
            color: var(--text-primary);
            font-size: 14px;
        }

        .modal-footer {
            display: flex;
            gap: 10px;
            justify-content: flex-end;
        }

        .btn-primary {
            background-color: #007bff;
        }

        .btn-primary:hover:not(:disabled) {
            background-color: #0056b3;
        }

        .btn-secondary {
            background-color: var(--bg-tertiary);
            color: var(--text-primary);
            border: 1px solid var(--border-color);
        }

        .btn-secondary:hover {
            background-color: var(--border-color);
        }

        @media (max-width: 768px) {
            .header {
                flex-direction: column;
                align-items: stretch;
            }

            .header-controls {
                flex-direction: column;
            }

            .search-box {
                min-width: 100%;
            }

            .container {
                flex-direction: column;
                gap: 15px;
                padding: 15px;
                min-height: auto;
            }

            .column {
                flex: 0 0 auto;
                min-height: 400px;
            }
        }

        @media (max-width: 480px) {
            .header h1 {
                font-size: 18px;
            }

            .column {
                flex: 0 0 auto;
            }

            .modal-content {
                padding: 20px;
            }
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>📊 Kanban Board</h1>
        <div class="header-controls">
            <input 
                type="text" 
                class="search-box" 
                id="searchInput" 
                placeholder="Search cards by title..."
            >
            <button class="btn" id="addCardBtn">+ Add Card</button>
            <button class="theme-toggle" id="themeToggle">🌙 Dark</button>
        </div>
    </div>

    <div class="container">
        <div class="column">
            <div class="column-header">
                <span class="column-title">To do</span>
                <span class="column-count" data-column="todo">0</span>
            </div>
            <div class="cards-container" data-column="todo"></div>
        </div>

        <div class="column">
            <div class="column-header">
                <span class="column-title">In progress</span>
                <span class="column-count" data-column="inprogress">0</span>
            </div>
            <div class="cards-container" data-column="inprogress"></div>
        </div>

        <div class="column">
            <div class="column-header">
                <span class="column-title">Done</span>
                <span class="column-count" data-column="done">0</span>
            </div>
            <div class="cards-container" data-column="done"></div>
        </div>
    </div>

    <div class="modal" id="addCardModal">
        <div class="modal-content">
            <div class="modal-header">
                <h2>Add New Card</h2>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label for="cardTitle">Title *</label>
                    <input 
                        type="text" 
                        id="cardTitle" 
                        placeholder="Enter card title..."
                    >
                </div>
                <div class="form-group">
                    <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-footer">
                <button class="btn btn-secondary" id="cancelBtn">Cancel</button>
                <button class="btn btn-primary" id="saveCardBtn" disabled>Save Card</button>
            </div>
        </div>
    </div>

    <script>
        let cards = [
            { id: 1, title: 'Design homepage mockup', priority: 'high', column: 'todo' },
            { id: 2, title: 'Review design feedback', priority: 'medium', column: 'todo' },
            { id: 3, title: 'Implement user authentication', priority: 'high', column: 'inprogress' },
            { id: 4, title: 'Set up database schema', priority: 'medium', column: 'inprogress' },
            { id: 5, title: 'Deploy to staging', priority: 'low', column: 'done' },
            { id: 6, title: 'Write API documentation', priority: 'medium', column: 'done' }
        ];

        let nextId = 7;
        let draggedCard = null;
        let searchTerm = '';

        const searchInput = document.getElementById('searchInput');
        const addCardBtn = document.getElementById('addCardBtn');
        const addCardModal = document.getElementById('addCardModal');
        const cardTitleInput = document.getElementById('cardTitle');
        const cardPriorityInput = document.getElementById('cardPriority');
        const saveCardBtn = document.getElementById('saveCardBtn');
        const cancelBtn = document.getElementById('cancelBtn');
        const themeToggle = document.getElementById('themeToggle');

        searchInput.addEventListener('input', (e) => {
            searchTerm = e.target.value.toLowerCase();
            render();
        });

        addCardBtn.addEventListener('click', () => {
            cardTitleInput.value = '';
            cardPriorityInput.value = 'medium';
            saveCardBtn.disabled = true;
            addCardModal.classList.add('show');
            cardTitleInput.focus();
        });

        cancelBtn.addEventListener('click', () => {
            addCardModal.classList.remove('show');
        });

        cardTitleInput.addEventListener('input', () => {
            saveCardBtn.disabled = cardTitleInput.value.trim() === '';
        });

        saveCardBtn.addEventListener('click', () => {
            const title = cardTitleInput.value.trim();
            if (title) {
                cards.push({
                    id: nextId++,
                    title,
                    priority: cardPriorityInput.value,
                    column: 'todo'
                });
                addCardModal.classList.remove('show');
                render();
            }
        });

        cardTitleInput.addEventListener('keypress', (e) => {
            if (e.key === 'Enter' && !saveCardBtn.disabled) {
                saveCardBtn.click();
            }
        });

        addCardModal.addEventListener('click', (e) => {
            if (e.target === addCardModal) {
                addCardModal.classList.remove('show');
            }
        });

        themeToggle.addEventListener('click', () => {
            document.body.classList.toggle('dark-theme');
            const isDark = document.body.classList.contains('dark-theme');
            themeToggle.textContent = isDark ? '☀️ Light' : '🌙 Dark';
        });

        function render() {
            ['todo', 'inprogress', 'done'].forEach(columnId => {
                const container = document.querySelector(`[data-column="${columnId}"]`);
                const countElement = document.querySelector(`.column-count[data-column="${columnId}"]`);
                
                const filteredCards = cards.filter(card => 
                    card.column === columnId &&
                    (searchTerm === '' || card.title.toLowerCase().includes(searchTerm))
                );
                
                const allCardsCount = cards.filter(card => card.column === columnId).length;
                countElement.textContent = allCardsCount;
                
                container.innerHTML = '';
                filteredCards.forEach(card => {
                    container.appendChild(createCardElement(card));
                });
            });

            setupDropZones();
        }

        function createCardElement(card) {
            const cardDiv = document.createElement('div');
            cardDiv.className = 'card';
            cardDiv.draggable = true;
            cardDiv.dataset.cardId = card.id;
            
            cardDiv.innerHTML = `
                <div class="card-title">${escapeHtml(card.title)}</div>
                <div class="card-footer">
                    <span class="priority-badge priority-${card.priority}">${card.priority}</span>
                    <button class="card-delete btn-small" data-card-id="${card.id}">Delete</button>
                </div>
            `;
            
            cardDiv.querySelector('.card-delete').addEventListener('click', (e) => {
                e.stopPropagation();
                cards = cards.filter(c => c.id !== card.id);
                render();
            });
            
            cardDiv.addEventListener('dragstart', (e) => {
                draggedCard = card;
                cardDiv.classList.add('dragging');
                e.dataTransfer.effectAllowed = 'move';
            });
            
            cardDiv.addEventListener('dragend', () => {
                cardDiv.classList.remove('dragging');
            });
            
            return cardDiv;
        }

        function escapeHtml(text) {
            const div = document.createElement('div');
            div.textContent = text;
            return div.innerHTML;
        }

        function setupDropZones() {
            document.querySelectorAll('.cards-container').forEach(container => {
                container.addEventListener('dragover', (e) => {
                    e.preventDefault();
                    e.dataTransfer.dropEffect = 'move';
                    if (draggedCard) {
                        container.style.backgroundColor = 'rgba(0, 123, 255, 0.1)';
                    }
                });
                
                container.addEventListener('dragleave', (e) => {
                    if (e.target === container) {
                        container.style.backgroundColor = '';
                    }
                });
                
                container.addEventListener('drop', (e) => {
                    e.preventDefault();
                    container.style.backgroundColor = '';
                    
                    if (draggedCard) {
                        draggedCard.column = container.dataset.column;
                        draggedCard = null;
                        render();
                    }
                });
            });
        }

        render();
    </script>
</body>
</html>
Reasoning trace (11,715 tokens)
Let me build an interactive Kanban board with the requirements:

1. Three columns with card counts
2. Pre-populated with 6 cards
3. Each card has title and priority badge
4. Add card via modal
5. Delete card with button
6. Drag and drop between columns
7. Search/filter by title
8. Light/dark theme toggle
9. Memory only state

I'll use:
- HTML for structure
- CSS for styling (including light/dark theme via CSS variables)
- Vanilla JavaScript for interactivity

Let me structure this:

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Kanban Board</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        :root {
            --bg-primary: #ffffff;
            --bg-secondary: #f5f5f5;
            --bg-tertiary: #e9ecef;
            --text-primary: #212529;
            --text-secondary: #6c757d;
            --border-color: #dee2e6;
            --shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
            --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.1);
        }

        body.dark-theme {
            --bg-primary: #1e1e1e;
            --bg-secondary: #2d2d2d;
            --bg-tertiary: #3a3a3a;
            --text-primary: #e0e0e0;
            --text-secondary: #9e9e9e;
            --border-color: #404040;
            --shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
            --shadow-lg: 0 4px 6px rgba(0, 0, 0, 0.3);
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
            background-color: var(--bg-primary);
            color: var(--text-primary);
            transition

Setting up CSS variables for both themes covers backgrounds, text, borders, and shadows consistently. Now I'll continue defining the body styles and transitions before moving into the column/card layout.

px 16px;
        }

Buttons get hover transitions, blue primary color with darker hover state. Small button variant with reduced padding. Theme toggle styled with border and background matching CSS variables. Container uses flexbox for horizontal scrolling columns, with min-height filling the viewport minus header space.

Columns get fixed width 350px, rounded corners, flex column layout for stacking header/cards. Column headers use flexbox with space-between for title and count, bottom border for separation. The count badge is circular, small, with contrasting background - styled like a notification bubble.

Cards container is scrollable with flex:1 to fill remaining space, custom thin scrollbar styling. Individual cards have subtle borders, rounded corners, hover effects with shadow and slight lift transform, and a dragging state with reduced opacity for drag-and-drop feedback.

For card contents, titles use medium font weight with word-break for long text, and footers use flexbox to space out elements like priority badges. Priority badges get color-coded styling for low/medium/high urgency levels, each with corresponding dark theme variants using appropriately muted background and text colors.

Beyond the priority badges, I'm adding delete buttons with red coloring and hover states, drop indicators for drag-and-drop card reordering (thin blue lines that toggle visibility), and now starting modal overlay styles with fixed positioning covering the full viewport and a semi-transparent dark backdrop.

The modal content gets rounded corners, padding, a max-width constraint, and a drop shadow for depth. I'm structuring header/body sections, then setting up form-group spacing with labels and input/select fields styled consistently using the border and background CSS variables.

Continuing into the modal footer, I align buttons to the right with gap spacing, and define primary/secondary button color states. The hidden utility class forces display:none for toggling. Finally, mobile breakpoints stack the header and controls vertically and expand the search box to full width.

button>
        </div>
    </div>

    <div class="container">
        <div class="column">
            <div class="column-header">
                <span class="column-title">To do</span>
                <span class="column-count" data-column="todo">2</span>
            </div>
            <div class="cards-container" data-column="todo"></div>
        </div>

I'm setting up three columns—To do, In progress, Done—each with a header showing title and card count, plus a container for the draggable cards.

                        <option value="low">Low</option>
                        <option value="medium" selected>Medium</option>
                        <option value="high">High</option>
                    </select>
                </div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-secondary" id="cancelBtn">Cancel</button>
                <button class="btn btn-primary" id="saveCardBtn">Save Card</button>
            </div>
        </div>
    </div>

Now setting up the JS state with sample cards seeded across todo, in-progress columns, giving each a title, priority level, and column assignment to populate the board initially.

For the done column, I'll add a couple cards like the staging deployment and API documentation task, then set up variables for tracking the next ID, drag state, and search term.

I'll grab references to all the key DOM elements - search input, modal controls, theme toggle - then wire up the search listener to filter cards as the user types, triggering a re-render on each keystroke.

Opening the add-card modal resets the inputs and focuses the title field. Cancel just hides it. Saving validates the title isn't empty, pushes a new card object with the next ID into the todo column, then closes and re-renders. Enter key in the title field triggers save too.

Clicking outside the modal closes it. Theme toggle flips a dark-theme class and swaps the button text/emoji.

The render function loops through columns, filters cards by search term, updates counts (using the unfiltered count), and rebuilds each container's cards. createCardElement builds the card markup with escaped title and priority badge, wires up delete (filters card out and re-renders) and drag events (tracking draggedCard/draggedFromColumn, toggling a dragging class).

Drop zones get dragover/dragleave/drop handlers that highlight the target with a background tint and, on drop, reassign the dragged card's column and re-render plus re-setup drop zones.

Checking against requirements - three columns with counts, pre-populated cards, priority badges, add/delete, drag-and-drop with visual feedback, search, theme toggle, in-memory state - all seem covered. The drop indicator via background-color change should suffice as the visual cue.

One issue: I used alert() for the empty-title case, which violates the no-alert constraint. I should replace it with either a red border or simply disabling the save button when the input is empty, using an input listener to toggle `saveCardBtn.disabled`. That's cleaner than relying on an alert, so I'll rewrite that handler to track button state as the user types and update the full HTML accordingly, including CSS variables for light/dark themes, header layout, search box, buttons, and theme toggle styling.