A fully-featured Kanban board with drag-and-drop, IndexedDB persistence, and rich task management built with vanilla JavaScript (ES6+).
index.html in your browserkanban-board/
āāā index.html
āāā src/
ā āāā core/ # Pure logic
ā ā āāā constants.js
ā ā āāā TaskLogic.js # Task operations
ā ā āāā ColumnLogic.js # Column operations
ā ā āāā utils.js
ā āāā services/ # External integrations
ā ā āāā IndexedDBService.js
ā āāā state/ # Orchestration
ā ā āāā BoardState.js
ā āāā ui/ # DOM & Events
ā ā āāā Renderer.js
ā ā āāā EventHandler.js
ā ā āāā DragDropHandler.js
ā āāā main.js
āāā styles/
āāā main.css
āāā components.css
āāā animations.css
State (BoardState)
ā
Rules (TaskLogic + ColumnLogic) ā Pure functions
ā
Actions (add, move, delete)
ā
UI (Renderer + DragDropHandler + EventHandler)
ā
Storage (IndexedDBService)
{
id: 1234567890.123,
title: "Implement feature",
description: "Add drag and drop",
assignee: "John Doe",
priority: "high", // low, medium, high
columnId: "in-progress",
startDate: "2024-01-01",
dueDate: "2024-01-15",
createdAt: 1234567890000,
updatedAt: 1234567890000,
order: 0
}
// Similar to Solitaire drag system
dragStart ā store task & column
ā
dragOver ā visual feedback
ā
drop ā move task to new column
ā
save to IndexedDB ā render
{
id: "custom-123",
name: "Review",
color: "#8b5cf6",
order: 2
}
async addTask(data) {
await this.storage.addTask(newTask);
await this.renderer.animateAddTask(newTask);
}
const { valid, error } = TaskLogic.validateTask(data);
const [columns, tasks] = await Promise.all([...]);
const movedTask = { ...task, columnId: newId };
this.tasks = [...this.tasks, newTask];
const columnTasks = tasks.filter(t => t.columnId === id);
columns.forEach(col => this.renderColumn(col));
const firstColumn = this.columns[0]?.id || 'backlog';
`<div class="task-card" data-id="${task.id}">`
import { TaskLogic } from '../core/TaskLogic.js';
export class BoardState { }
Based on Solitaire's drag system:
// 1. Make cards draggable
<div class="task-card" draggable="true">
// 2. Track drag state
onDragStart(e, task) {
this.draggedTask = task;
e.target.classList.add('dragging');
}
// 3. Handle drop zones
onDragOver(e, columnId) {
e.preventDefault();
column.classList.add('drag-over');
}
// 4. Complete the move
async onDrop(e, columnId) {
await this.board.moveTask(taskId, columnId);
}
Two Object Stores:
Indexes:
columnId, priorityorderDefault Columns:
Built with ā¤ļø using vanilla JavaScript, ES6+, and IndexedDB