We’re building an Interactive Todo List using Vanilla JavaScript — no frameworks, no fluff, just clean HTML, CSS, and pure JavaScript magic.
This project is fun, fast, and very real-world. Perfect for your portfolio!






Features:
- Add new tasks
- Mark tasks as completed
- Delete tasks
- Filter: All / Active / Completed
- Persistent with
localStorage
We’ll Build It in 5 Steps:
- HTML Structure – Input, buttons, list
- CSS Styling – Clean and responsive
- JS: Add/Delete/Check Tasks
- JS: Filter and Manage State
- LocalStorage for Saving Tasks
Shall we begin with Step 1: HTML structure? Let’s lay down the bones of our Interactive Todo List.
Step 1: HTML Structure
Here’s the clean and semantic HTML layout for our todo app:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Interactive Todo List</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container">
<h1>My Todo List</h1>
<div class="todo-input">
<input type="text" id="task-input" placeholder="What do you need to do?" />
<button id="add-task">Add</button>
</div>
<ul id="todo-list"></ul>
<div class="todo-footer">
<button id="show-all">All</button>
<button id="show-active">Active</button>
<button id="show-completed">Completed</button>
<button id="clear-completed">Clear Completed</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
Code language: HTML, XML (xml)
Quick Overview:
#task-input
: Where users type a task#add-task
: Button to add the task#todo-list
: Where tasks appear- Footer buttons for filtering and clearing
Up next? ✨
CSS Styling to make it look modern and clean.
Let’s style it up! 🎨💫
Here’s a clean, modern CSS to give your Interactive Todo List a sleek and functional look.
Step 2: CSS Styling (style.css
)
/* Google Font for a modern look */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #f0f2f5;
font-family: 'Inter', sans-serif;
display: flex;
justify-content: center;
align-items: flex-start;
min-height: 100vh;
padding: 40px;
}
.container {
background: #fff;
padding: 30px 25px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
width: 100%;
max-width: 500px;
}
h1 {
margin-bottom: 20px;
color: #333;
font-size: 28px;
text-align: center;
}
.todo-input {
display: flex;
gap: 10px;
margin-bottom: 20px;
}
#task-input {
flex: 1;
padding: 12px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 16px;
}
#add-task {
padding: 12px 18px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
transition: background 0.3s ease;
}
#add-task:hover {
background-color: #43a047;
}
#todo-list {
list-style: none;
}
#todo-list li {
background: #f9f9f9;
padding: 12px;
margin-bottom: 10px;
border-radius: 6px;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.2s ease-in-out;
}
#todo-list li.completed {
text-decoration: line-through;
color: #888;
background: #e2e2e2;
}
.todo-footer {
margin-top: 20px;
display: flex;
gap: 10px;
flex-wrap: wrap;
justify-content: space-between;
}
.todo-footer button {
flex: 1;
padding: 10px;
border: none;
background-color: #e0e0e0;
color: #333;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s ease;
}
.todo-footer button:hover {
background-color: #d5d5d5;
}
Code language: CSS (css)
Your todo list should now look clean, centered, and responsive — ready for some interactivity. 🎯
Ready for the fun part?
Yessss! Let’s bring this Todo List to life with Vanilla JavaScript 🧠⚡
This script will handle:
- Adding tasks
- Marking them as completed
- Deleting tasks
- Filtering: All / Active / Completed
- Saving to
localStorage
Step 3: JavaScript Logic (script.js
)
// Select DOM elements
const input = document.getElementById("task-input");
const addBtn = document.getElementById("add-task");
const list = document.getElementById("todo-list");
const showAll = document.getElementById("show-all");
const showActive = document.getElementById("show-active");
const showCompleted = document.getElementById("show-completed");
const clearCompleted = document.getElementById("clear-completed");
// Load from localStorage
let todos = JSON.parse(localStorage.getItem("todos")) || [];
// Render tasks
function renderTodos(filter = "all") {
list.innerHTML = "";
todos.forEach((todo, index) => {
if (
(filter === "active" && todo.completed) ||
(filter === "completed" && !todo.completed)
) return;
const li = document.createElement("li");
li.className = todo.completed ? "completed" : "";
li.innerHTML = `
<span>${todo.text}</span>
<div>
<button onclick="toggleComplete(${index})">✔️</button>
<button onclick="deleteTodo(${index})">🗑️</button>
</div>
`;
list.appendChild(li);
});
}
// Add new task
addBtn.addEventListener("click", () => {
const text = input.value.trim();
if (text !== "") {
todos.push({ text, completed: false });
input.value = "";
saveAndRender();
}
});
// Mark task as complete/incomplete
function toggleComplete(index) {
todos[index].completed = !todos[index].completed;
saveAndRender();
}
// Delete task
function deleteTodo(index) {
todos.splice(index, 1);
saveAndRender();
}
// Save to localStorage and re-render
function saveAndRender() {
localStorage.setItem("todos", JSON.stringify(todos));
renderTodos(currentFilter);
}
// Filters
let currentFilter = "all";
showAll.addEventListener("click", () => {
currentFilter = "all";
renderTodos("all");
});
showActive.addEventListener("click", () => {
currentFilter = "active";
renderTodos("active");
});
showCompleted.addEventListener("click", () => {
currentFilter = "completed";
renderTodos("completed");
});
clearCompleted.addEventListener("click", () => {
todos = todos.filter(todo => !todo.completed);
saveAndRender();
});
// Initial load
renderTodos();
Code language: JavaScript (javascript)
🎉 Done!
You now have a fully interactive, filterable, and persistent Todo List App — built with pure HTML, CSS, and JavaScript.
Discover more from Prime Inspire
Subscribe to get the latest posts sent to your email.