PHP To-Do App with Drag & Drop Animation
Build a Kanban-style to-do app with drag-and-drop task management, smooth animations, and PHP backend persistence.
Overview
Build a Kanban-style to-do app with three columns (To Do, In Progress, Done). Users can drag tasks between columns with smooth animations, and PHP persists data to a JSON file.
Project Structure
todo-kanban/
├── index.php
├── api.php
├── data/tasks.json
└── css/style.cssBackend API (api.php)
<?php
header("Content-Type: application/json");
$file = "data/tasks.json";
if (!file_exists($file)) file_put_contents($file, json_encode(["todo"=>[],"progress"=>[],"done"=>[]]));
if ($_SERVER["REQUEST_METHOD"] === "GET") {
echo file_get_contents($file);
exit;
}
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$input = json_decode(file_get_contents("php://input"), true);
$action = $input["action"] ?? "";
$data = json_decode(file_get_contents($file), true);
switch ($action) {
case "add":
$data["todo"][] = [
"id" => uniqid(),
"text" => htmlspecialchars($input["text"] ?? ""),
"created" => date("Y-m-d H:i"),
];
break;
case "move":
$taskId = $input["taskId"];
$from = $input["from"];
$to = $input["to"];
foreach ($data[$from] as $i => $task) {
if ($task["id"] === $taskId) {
$data[$to][] = $task;
array_splice($data[$from], $i, 1);
break;
}
}
break;
case "delete":
$taskId = $input["taskId"];
$col = $input["column"];
$data[$col] = array_values(array_filter($data[$col], fn($t) => $t["id"] !== $taskId));
break;
}
file_put_contents($file, json_encode($data));
echo json_encode(["ok" => true]);
}
?>Main Page (index.php)
<?php $tasks = json_decode(file_get_contents("data/tasks.json"), true); ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kanban To-Do</title>
<style>
body { background: #0a0a23; color: #fff; font-family: sans-serif; padding: 2rem; }
h1 { text-align: center; margin-bottom: 2rem; }
.board { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; max-width: 900px; margin: 0 auto; }
.column { background: #1a1a3e; border-radius: 16px; padding: 1rem; min-height: 400px; }
.column h2 { text-align: center; font-size: 1.1rem; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 2px solid #2d2d5e; }
.task { background: #2d2d5e; padding: 12px; border-radius: 10px; margin-bottom: 8px; cursor: grab; transition: all 0.2s; }
.task:hover { transform: translateY(-2px); box-shadow: 0 4px 15px rgba(108,99,255,0.3); }
.task.dragging { opacity: 0.5; transform: rotate(3deg); }
.column.over { border: 2px dashed #6c63ff; }
.add-form { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
.add-form input { flex: 1; padding: 8px; border-radius: 8px; border: none; background: #2d2d5e; color: #fff; }
.add-form button { padding: 8px 16px; background: #6c63ff; color: #fff; border: none; border-radius: 8px; cursor: pointer; }
@keyframes slideIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } }
.task { animation: slideIn 0.3s ease; }
</style>
</head>
<body>
<h1>Kanban To-Do Board</h1>
<div class="board">
<div class="column" data-col="todo" ondragover="e(event)" ondrop="drop(event)">
<h2>To Do</h2>
<div class="add-form">
<input id="newTask" placeholder="New task...">
<button onclick="addTask()">+</button>
</div>
<div class="tasks" id="todo">
<?php foreach ($tasks["todo"] as $t): ?>
<div class="task" draggable="true" data-id="<?= $t["id"] ?>"><?= $t["text"] ?></div>
<?php endforeach; ?>
</div>
</div>
<div class="column" data-col="progress" ondragover="e(event)" ondrop="drop(event)">
<h2>In Progress</h2>
<div class="tasks" id="progress">
<?php foreach ($tasks["progress"] as $t): ?>
<div class="task" draggable="true" data-id="<?= $t["id"] ?>"><?= $t["text"] ?></div>
<?php endforeach; ?>
</div>
</div>
<div class="column" data-col="done" ondragover="e(event)" ondrop="drop(event)">
<h2>Done</h2>
<div class="tasks" id="done">
<?php foreach ($tasks["done"] as $t): ?>
<div class="task" draggable="true" data-id="<?= $t["id"] ?>"><?= $t["text"] ?></div>
<?php endforeach; ?>
</div>
</div>
</div>
<script>
let draggedTask = null, fromCol = null;
document.querySelectorAll(".task").forEach(initDrag);
function initDrag(task) {
task.addEventListener("dragstart", ev => {
draggedTask = task; fromCol = task.closest(".column").dataset.col;
task.classList.add("dragging");
});
task.addEventListener("dragend", () => task.classList.remove("dragging"));
}
function e(ev) { ev.preventDefault(); ev.currentTarget.classList.add("over"); }
async function drop(ev) {
ev.preventDefault();
const col = ev.currentTarget;
col.classList.remove("over");
const toCol = col.dataset.col;
col.querySelector(".tasks").appendChild(draggedTask);
await fetch("api.php", { method: "POST", headers: {"Content-Type":"application/json"}, body: JSON.stringify({ action:"move", taskId: draggedTask.dataset.id, from: fromCol, to: toCol }) });
}
async function addTask() {
const input = document.getElementById("newTask");
if (!input.value.trim()) return;
await fetch("api.php", { method:"POST", headers:{"Content-Type":"application/json"}, body: JSON.stringify({ action:"add", text: input.value }) });
location.reload();
}
</script>
</body>
</html>Technologies
- PHP — JSON file persistence, REST API - HTML5 Drag & Drop API — native drag events - CSS — slide-in animations, hover effects - JavaScript — drag handlers, async API calls
Related Projects
Analog Clock with PHP & CSS
Build a beautiful real-time analog clock using PHP for time calculation and CSS for the rotating hands and dial.
Animated Loading Spinners Gallery in PHP
Create a gallery of 10+ beautiful CSS loading spinner animations served dynamically through PHP.
PHP Color Palette Generator
Generate beautiful random color palettes with hex codes, RGB values, and one-click copy — all powered by PHP.
Comments (0)
No comments yet. Be the first to comment!