Qwen3 Coder Next
by Qwenreasoning defaultClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 1 min 12 s
- Cost
- $0.0057
- Output tokens
- 7,053
- Spent thinking
- 0 tokens
- Speed
- 97.9 tok/s
- Console errors
- 0
- First token after
- 392 ms
- First visible text after
- 392 ms
- Prompt tokens
- 498
- Code size
- 29.5 KB
- Finish reason
- stop
- Served by
- Parasail
- Model id
qwen/qwen3-coder-next- Reasoning sent
model default- Max output
- 235,929 tokens
- Generated
- Nov 13, 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 · 859 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Kanban Board</title>
<style>
:root {
--primary-color: #6366f1;
--primary-hover: #4f46e5;
--bg-color: #f3f4f6;
--text-color: #1f2937;
--column-bg: #e5e7eb;
--card-bg: #ffffff;
--card-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--border-color: #d1d5db;
--modal-overlay: rgba(0, 0, 0, 0.5);
--drop-indicator-color: #6366f1;
/* Priority Colors */
--low-color: #10b981;
--med-color: #f59e0b;
--high-color: #ef4444;
/* Dark Theme Variables */
--dark-bg-color: #111827;
--dark-text-color: #f3f4f6;
--dark-column-bg: #1f2937;
--dark-card-bg: #374151;
--dark-border-color: #374151;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}
body {
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s ease, color 0.3s ease;
padding: 20px;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
body.dark-mode {
background-color: var(--dark-bg-color);
color: var(--dark-text-color);
}
/* Header & Controls */
header {
width: 100%;
max-width: 1200px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
flex-wrap: wrap;
gap: 15px;
}
h1 {
font-size: 2rem;
font-weight: 800;
color: var(--primary-color);
}
.controls {
display: flex;
gap: 15px;
align-items: center;
}
.search-box {
position: relative;
}
.search-box input {
padding: 10px 15px;
padding-left: 35px;
border-radius: 8px;
border: 1px solid var(--border-color);
background-color: var(--card-bg);
color: var(--text-color);
font-size: 1rem;
width: 200px;
transition: all 0.3s;
}
.search-box input:focus {
outline: 2px solid var(--primary-color);
border-color: transparent;
}
.search-icon {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
width: 16px;
height: 16px;
fill: var(--text-color);
opacity: 0.7;
}
.theme-toggle {
background: none;
border: 1px solid var(--border-color);
color: var(--text-color);
padding: 8px 16px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: background 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.theme-toggle:hover {
background-color: var(--border-color);
}
.btn-add {
background-color: var(--primary-color);
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
display: flex;
align-items: center;
gap: 8px;
}
.btn-add:hover {
background-color: var(--primary-hover);
}
/* Kanban Board */
.board {
display: flex;
gap: 30px;
width: 100%;
max-width: 1200px;
overflow-x: auto;
padding-bottom: 20px;
}
.column {
flex: 1;
background-color: var(--column-bg);
border-radius: 12px;
padding: 20px;
min-width: 300px;
display: flex;
flex-direction: column;
transition: background-color 0.3s;
}
body.dark-mode .column {
background-color: var(--dark-column-bg);
}
.column-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid var(--border-color);
}
body.dark-mode .column-header {
border-bottom-color: var(--dark-border-color);
}
.column-title {
font-size: 1.25rem;
font-weight: 700;
}
.card-count {
background-color: var(--text-color);
color: var(--bg-color);
padding: 2px 8px;
border-radius: 12px;
font-size: 0.85rem;
font-weight: 600;
min-width: 24px;
text-align: center;
}
body.dark-mode .card-count {
background-color: var(--dark-text-color);
color: var(--dark-bg-color);
}
.card-list {
flex-grow: 1;
display: flex;
flex-direction: column;
gap: 15px;
min-height: 200px; /* Allows dropping into empty columns */
}
/* Cards */
.card {
background-color: var(--card-bg);
border-radius: 8px;
padding: 16px;
box-shadow: var(--card-shadow);
cursor: grab;
transition: transform 0.2s, box-shadow 0.2s, opacity 0.2s;
position: relative;
border-left: 4px solid var(--card-border-color);
display: flex;
flex-direction: column;
gap: 8px;
}
body.dark-mode .card {
background-color: var(--dark-card-bg);
border-left-color: var(--dark-border-color);
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.card.dragging {
opacity: 0.5;
cursor: grabbing;
}
.card-title {
font-size: 1rem;
font-weight: 600;
line-height: 1.4;
}
.card-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: auto;
}
.priority-badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
color: white;
}
.priority-low { background-color: var(--low-color); }
.priority-medium { background-color: var(--med-color); }
.priority-high { background-color: var(--high-color); }
.card-actions {
display: flex;
justify-content: flex-end;
}
.btn-delete {
background: none;
border: none;
color: var(--high-color);
cursor: pointer;
padding: 4px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: background 0.2s;
}
.btn-delete:hover {
background-color: #fef2f2;
}
body.dark-mode .btn-delete:hover {
background-color: #374151;
}
.btn-delete svg {
width: 16px;
height: 16px;
}
/* Drop Indicator */
.drop-indicator {
height: 4px;
background-color: var(--drop-indicator-color);
border-radius: 2px;
margin: 8px 0;
transition: opacity 0.2s;
}
.column.drag-over .drop-indicator {
opacity: 1;
}
.column.drag-over {
background-color: rgba(99, 102, 241, 0.1);
}
body.dark-mode .column.drag-over {
background-color: rgba(99, 102, 241, 0.2);
}
/* Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--modal-overlay);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s, visibility 0.3s;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background-color: var(--card-bg);
padding: 30px;
border-radius: 16px;
width: 90%;
max-width: 400px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
transform: scale(0.9);
transition: transform 0.3s;
}
body.dark-mode .modal {
background-color: var(--dark-card-bg);
}
.modal-overlay.active .modal {
transform: scale(1);
}
.modal h2 {
margin-bottom: 20px;
color: var(--text-color);
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: var(--text-color);
}
.form-group input,
.form-group select {
width: 100%;
padding: 10px;
border: 1px solid var(--border-color);
border-radius: 8px;
background-color: var(--bg-color);
color: var(--text-color);
font-size: 1rem;
}
.form-group input:focus,
.form-group select:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2);
}
.modal-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 25px;
}
.btn-cancel {
background-color: #e5e7eb;
color: var(--text-color);
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
}
body.dark-mode .btn-cancel {
background-color: #374151;
color: var(--dark-text-color);
}
.btn-save {
background-color: var(--primary-color);
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
}
/* Initial State Animation Hint */
.fade-in {
animation: fadeIn 0.5s ease-out forwards;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
/* Responsive adjustments */
@media (max-width: 768px) {
.board {
flex-direction: column;
}
.controls {
width: 100%;
justify-content: space-between;
}
.search-box input {
width: 60%;
}
}
</style>
</head>
<body>
<header>
<h1>Kanban Board</h1>
<div class="controls">
<div class="search-box">
<svg class="search-icon" viewBox="0 0 24 24">
<path d="M21.71 20.29l-5.01-5.01c1.11-2.06 1.68-4.37 1.68-6.75 0-5.52-4.48-10-10-10S0 4.48 0 10s4.48 10 10 10c2.39 0 4.7-0.57 6.75-1.68l5.01 5.01c0.39 0.39 1.02 0.39 1.41 0 0.4-0.39 0.4-1.02 0-1.41zM10 16c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6z"/>
</svg>
<input type="text" id="searchInput" placeholder="Search tasks..." />
</div>
<button class="theme-toggle" id="themeToggle" aria-label="Toggle Dark Mode">
<svg id="moonIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none;"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
<svg id="sunIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>
Theme
</button>
<button class="btn-add" id="addCardBtn">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
Add Card
</button>
</div>
</header>
<main class="board">
<!-- To Do Column -->
<div class="column" id="todo" data-status="todo">
<div class="column-header">
<span class="column-title">To Do</span>
<span class="card-count" id="count-todo">0</span>
</div>
<div class="card-list" id="list-todo">
<div class="drop-indicator" style="opacity: 0;"></div>
</div>
</div>
<!-- In Progress Column -->
<div class="column" id="inprogress" data-status="inprogress">
<div class="column-header">
<span class="column-title">In Progress</span>
<span class="card-count" id="count-inprogress">0</span>
</div>
<div class="card-list" id="list-inprogress">
<div class="drop-indicator" style="opacity: 0;"></div>
</div>
</div>
<!-- Done Column -->
<div class="column" id="done" data-status="done">
<div class="column-header">
<span class="column-title">Done</span>
<span class="card-count" id="count-done">0</span>
</div>
<div class="card-list" id="list-done">
<div class="drop-indicator" style="opacity: 0;"></div>
</div>
</div>
</main>
<!-- Add Card Modal -->
<div class="modal-overlay" id="addModal">
<div class="modal">
<h2>Add New Task</h2>
<form id="addCardForm">
<div class="form-group">
<label for="cardTitle">Task Title</label>
<input type="text" id="cardTitle" placeholder="What needs to be done?" required maxlength="100">
</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 class="form-group">
<label for="cardColumn">Start In Column</label>
<select id="cardColumn">
<option value="todo" selected>To Do</option>
<option value="inprogress">In Progress</option>
<option value="done">Done</option>
</select>
</div>
<div class="modal-actions">
<button type="button" class="btn-cancel" id="cancelAddBtn">Cancel</button>
<button type="submit" class="btn-save">Create Task</button>
</div>
</form>
</div>
</div>
<script>
// --- State Management ---
const initialState = [
{ id: 1, title: "Research Competitors", priority: "medium", status: "todo" },
{ id: 2, title: "Design System Draft", priority: "high", status: "todo" },
{ id: 3, title: "Setup Repository", priority: "low", status: "todo" },
{ id: 4, title: "Implement Auth Logic", priority: "high", status: "inprogress" },
{ id: 5, title: "Database Schema", priority: "high", status: "inprogress" },
{ id: 6, title: "User Testing (Phase 1)", priority: "medium", status: "done" }
];
let cards = JSON.parse(JSON.stringify(initialState));
let draggedCard = null;
let dragOverTarget = null;
let currentDarkMode = false;
// --- DOM Elements ---
const columns = {
todo: document.getElementById('list-todo'),
inprogress: document.getElementById('list-inprogress'),
done: document.getElementById('list-done')
};
const counts = {
todo: document.getElementById('count-todo'),
inprogress: document.getElementById('count-inprogress'),
done: document.getElementById('count-done')
};
const searchInput = document.getElementById('searchInput');
const themeToggle = document.getElementById('themeToggle');
const addCardBtn = document.getElementById('addCardBtn');
const addModal = document.getElementById('addModal');
const cancelAddBtn = document.getElementById('cancelAddBtn');
const addCardForm = document.getElementById('addCardForm');
// --- Icons (SVG) ---
const trashIcon = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>`;
const closeIcon = `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
// --- Initialization ---
function init() {
renderCards();
setupEventListeners();
updateCounts();
// Simulate a user clicking the page to activate focus/clicks for the iframe
document.body.addEventListener('click', function() {
document.body.removeEventListener('click', arguments.callee);
}, { once: true });
}
// --- Rendering ---
function renderCards(filterText = '') {
// Clear columns
Object.values(columns).forEach(col => col.innerHTML = '<div class="drop-indicator" style="opacity: 0;"></div>');
// Filter and Sort
const filteredCards = cards.filter(card =>
card.title.toLowerCase().includes(filterText.toLowerCase())
);
// Create Card Elements
const cardElements = filteredCards.map(createCardElement);
// Append to columns
cardElements.forEach(el => {
const status = el.dataset.status;
if (columns[status]) {
columns[status].appendChild(el);
}
});
updateCounts();
setupDragAndDrop();
}
function createCardElement(card) {
const cardDiv = document.createElement('div');
cardDiv.className = 'card fade-in';
cardDiv.draggable = true;
cardDiv.id = card.id;
cardDiv.dataset.status = card.status;
// Dynamic border color based on priority
let borderColor = '';
if (card.priority === 'low') borderColor = 'var(--low-color)';
else if (card.priority === 'medium') borderColor = 'var(--med-color)';
else borderColor = 'var(--high-color)';
cardDiv.style.borderLeftColor = borderColor;
cardDiv.innerHTML = `
<div class="card-title">${card.title}</div>
<div class="card-meta">
<span class="priority-badge priority-${card.priority}">${card.priority}</span>
</div>
<div class="card-actions">
<button class="btn-delete" onclick="deleteCard(${card.id})" title="Delete Task">
${trashIcon}
</button>
</div>
`;
return cardDiv;
}
function updateCounts() {
Object.keys(counts).forEach(key => {
counts[key].textContent = cards.filter(c => c.status === key).length;
});
}
// --- Core Logic ---
function deleteCard(id) {
const cardIndex = cards.findIndex(c => c.id === id);
if (cardIndex > -1) {
cards.splice(cardIndex, 1);
renderCards(searchInput.value);
}
}
function addCard(title, priority, status) {
const newId = Math.max(...cards.map(c => c.id), 0) + 1;
cards.push({ id: newId, title, priority, status });
renderCards(searchInput.value);
}
// --- Drag & Drop Logic ---
function setupDragAndDrop() {
const cardsList = document.querySelectorAll('.card');
cardsList.forEach(card => {
card.addEventListener('dragstart', handleDragStart);
card.addEventListener('dragend', handleDragEnd);
card.addEventListener('dragover', handleDragOver);
card.addEventListener('dragleave', handleDragLeave);
card.addEventListener('drop', handleDrop);
// Touch support basics (prevent default to allow dragging on mobile if we had a custom touch logic)
// For this demo, we focus on Mouse, but basic touch prevention helps
card.addEventListener('touchstart', (e) => {
// e.preventDefault(); // Only if not scrolling
}, {passive: false});
});
// Allow dropping onto the column container itself (empty space)
const columnLists = document.querySelectorAll('.card-list');
columnLists.forEach(list => {
list.addEventListener('dragover', (e) => {
e.preventDefault(); // Necessary to allow dropping
const afterElement = getDragAfterElement(list, e.clientY);
const dragIndicator = list.querySelector('.drop-indicator');
if (afterElement == null) {
list.appendChild(dragIndicator);
} else {
list.insertBefore(dragIndicator, afterElement);
}
list.classList.add('drag-over');
});
list.addEventListener('dragleave', () => {
const dragIndicator = list.querySelector('.drop-indicator');
dragIndicator.style.opacity = '0';
list.classList.remove('drag-over');
});
});
}
function handleDragStart(e) {
draggedCard = this;
this.classList.add('dragging');
// Set data for compatibility
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', this.id);
// Hide the ghost image slightly or customize it (optional, browser support varies)
// e.dataTransfer.setDragImage(this, 0, 0);
}
function handleDragEnd(e) {
this.classList.remove('dragging');
// Clean up all indicators
document.querySelectorAll('.drop-indicator').forEach(el => el.style.opacity = '0');
document.querySelectorAll('.card-list').forEach(el => el.classList.remove('drag-over'));
draggedCard = null;
}
function handleDragOver(e) {
e.preventDefault(); // Necessary to allow dropping
if (draggedCard && this !== draggedCard) {
// Visual feedback is handled in the column listener usually,
// but we can highlight the specific card position here if needed
// For this simple implementation, the column highlight is enough
}
}
function handleDragLeave(e) {
// Handled by column container listeners mostly
}
function handleDrop(e) {
e.preventDefault();
if (draggedCard) {
// Find the column container of the drop target
let targetColumn = this.closest('.card-list');
if (targetColumn) {
// Get current status of dragged card
const oldStatus = draggedCard.dataset.status;
// Determine new position relative to drop indicator
const dragIndicator = targetColumn.querySelector('.drop-indicator');
const newStatus = targetColumn.parentElement.dataset.status; // e.g., "todo"
// Move the card DOM element
if (dragIndicator) {
targetColumn.insertBefore(draggedCard, dragIndicator);
dragIndicator.style.opacity = '0'; // Hide indicator after drop
} else {
targetColumn.appendChild(draggedCard);
}
// Update State
const cardIndex = cards.findIndex(c => c.id === parseInt(draggedCard.id));
if (cardIndex > -1) {
cards[cardIndex].status = newStatus;
draggedCard.dataset.status = newStatus;
// Re-render to ensure counts update and state is perfectly synced
// (Optional: direct DOM manipulation is faster, but render ensures counts/labels are perfect)
// For performance in huge lists, update state and counts manually.
// For this scale, renderCards is fine.
renderCards(searchInput.value);
}
}
}
}
// Helper to find position to insert before
function getDragAfterElement(container, y) {
const draggableElements = [...container.querySelectorAll('.card:not(.dragging)')];
return draggableElements.reduce((closest, child) => {
const box = child.getBoundingClientRect();
const offset = y - box.top - box.height / 2;
if (offset < 0 && offset > closest.offset) {
return { offset: offset, element: child };
} else {
return closest;
}
}, { offset: Number.NEGATIVE_INFINITY }).element;
}
// --- Modal Logic ---
function openModal() {
addModal.classList.add('active');
document.getElementById('cardTitle').focus();
}
function closeModal() {
addModal.classList.remove('active');
addCardForm.reset();
}
// --- Event Listeners ---
function setupEventListeners() {
// Search
searchInput.addEventListener('input', (e) => {
renderCards(e.target.value);
});
// Theme Toggle
themeToggle.addEventListener('click', () => {
currentDarkMode = !currentDarkMode;
if (currentDarkMode) {
document.body.classList.add('dark-mode');
document.getElementById('moonIcon').style.display = 'block';
document.getElementById('sunIcon').style.display = 'none';
} else {
document.body.classList.remove('dark-mode');
document.getElementById('moonIcon').style.display = 'none';
document.getElementById('sunIcon').style.display = 'block';
}
});
// Add Card
addCardBtn.addEventListener('click', openModal);
cancelAddBtn.addEventListener('click', closeModal);
addModal.addEventListener('click', (e) => {
if (e.target === addModal) closeModal();
});
addCardForm.addEventListener('submit', (e) => {
e.preventDefault();
const title = document.getElementById('cardTitle').value.trim();
const priority = document.getElementById('cardPriority').value;
const status = document.getElementById('cardColumn').value;
if (title) {
addCard(title, priority, status);
closeModal();
}
});
}
// Run
init();
</script>
</body>
</html>