Create a modern todo app with task management and filtering capabilities.
This project will guide you through creating a modern todo app interface using CSS. You'll learn to design task lists, input forms, and interactive elements.
Beginner: 1-2 hours | Intermediate: 1 hour
Difficulty Level: Beginner to Intermediate
<div class="todo-app">
<header class="todo-header">
<h1>My Todo List</h1>
<form class="todo-form">
<input type="text" placeholder="Add a new task..." class="todo-input">
<button type="submit" class="todo-btn">Add</button>
</form>
</header>
<div class="todo-filters">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="active">Active</button>
<button class="filter-btn" data-filter="completed">Completed</button>
</div>
<ul class="todo-list">
<li class="todo-item">
<input type="checkbox" class="todo-checkbox">
<span class="todo-text">Learn CSS Grid</span>
<button class="todo-delete"><i class="fas fa-trash"></i></button>
</li>
<li class="todo-item completed">
<input type="checkbox" class="todo-checkbox" checked>
<span class="todo-text">Build responsive layout</span>
<button class="todo-delete"><i class="fas fa-trash"></i></button>
</li>
</ul>
<div class="todo-stats">
<span>3 tasks remaining</span>
<button class="clear-completed">Clear completed</button>
</div>
</div>
.todo-app {
max-width: 600px;
margin: 2rem auto;
background: white;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
overflow: hidden;
}
.todo-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem;
text-align: center;
}
.todo-header h1 {
margin-bottom: 1.5rem;
font-size: 2rem;
}
.todo-form {
display: flex;
gap: 1rem;
}
.todo-input {
flex: 1;
padding: 1rem;
border: none;
border-radius: 10px;
font-size: 1rem;
outline: none;
}
.todo-btn {
background: rgba(255,255,255,0.2);
color: white;
border: none;
padding: 1rem 2rem;
border-radius: 10px;
cursor: pointer;
font-weight: 600;
transition: background 0.2s;
}
.todo-btn:hover {
background: rgba(255,255,255,0.3);
}
.todo-filters {
display: flex;
justify-content: center;
gap: 1rem;
padding: 1.5rem;
background: #f8f9fa;
}
.filter-btn {
background: white;
border: 2px solid #e9ecef;
padding: 0.5rem 1rem;
border-radius: 20px;
cursor: pointer;
transition: all 0.2s;
}
.filter-btn.active,
.filter-btn:hover {
background: #667eea;
color: white;
border-color: #667eea;
}
.todo-list {
list-style: none;
padding: 0;
margin: 0;
}
.todo-item {
display: flex;
align-items: center;
padding: 1rem 1.5rem;
border-bottom: 1px solid #f0f0f0;
transition: background 0.2s;
}
.todo-item:hover {
background: #f8f9fa;
}
.todo-checkbox {
margin-right: 1rem;
transform: scale(1.2);
}
.todo-text {
flex: 1;
font-size: 1rem;
color: #333;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: #999;
}
.todo-delete {
background: none;
border: none;
color: #e74c3c;
cursor: pointer;
padding: 0.5rem;
border-radius: 5px;
transition: background 0.2s;
}
.todo-delete:hover {
background: #ffe6e6;
}
.todo-stats {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
background: #f8f9fa;
color: #666;
}
.clear-completed {
background: none;
border: none;
color: #e74c3c;
cursor: pointer;
text-decoration: underline;
}
.clear-completed:hover {
color: #c0392b;
}
@media (max-width: 768px) {
.todo-app {
margin: 1rem;
}
.todo-form {
flex-direction: column;
}
.todo-filters {
flex-wrap: wrap;
}
.todo-stats {
flex-direction: column;
gap: 1rem;
text-align: center;
}
}
class TodoApp {
constructor() {
this.todos = [];
this.filter = 'all';
this.init();
}
init() {
this.bindEvents();
this.render();
}
bindEvents() {
document.querySelector('.todo-form').addEventListener('submit', (e) => {
e.preventDefault();
this.addTodo();
});
document.querySelector('.todo-list').addEventListener('click', (e) => {
if (e.target.classList.contains('todo-checkbox')) {
this.toggleTodo(e.target);
} else if (e.target.closest('.todo-delete')) {
this.deleteTodo(e.target.closest('.todo-item'));
}
});
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
this.setFilter(e.target.dataset.filter);
});
});
}
addTodo() {
const input = document.querySelector('.todo-input');
const text = input.value.trim();
if (text) {
this.todos.push({ id: Date.now(), text, completed: false });
input.value = '';
this.render();
}
}
toggleTodo(checkbox) {
const todoItem = checkbox.closest('.todo-item');
const todoId = parseInt(todoItem.dataset.id);
const todo = this.todos.find(t => t.id === todoId);
if (todo) {
todo.completed = !todo.completed;
this.render();
}
}
deleteTodo(todoItem) {
const todoId = parseInt(todoItem.dataset.id);
this.todos = this.todos.filter(t => t.id !== todoId);
this.render();
}
setFilter(filter) {
this.filter = filter;
document.querySelectorAll('.filter-btn').forEach(btn => {
btn.classList.toggle('active', btn.dataset.filter === filter);
});
this.render();
}
render() {
const todoList = document.querySelector('.todo-list');
const filteredTodos = this.filter === 'all' ? this.todos :
this.filter === 'active' ? this.todos.filter(t => !t.completed) :
this.todos.filter(t => t.completed);
todoList.innerHTML = filteredTodos.map(todo => `
<li class="todo-item ${todo.completed ? 'completed' : ''}" data-id="${todo.id}">
<input type="checkbox" class="todo-checkbox" ${todo.completed ? 'checked' : ''}>
<span class="todo-text">${todo.text}</span>
<button class="todo-delete"><i class="fas fa-trash"></i></button>
</li>
`).join('');
const remaining = this.todos.filter(t => !t.completed).length;
document.querySelector('.todo-stats span').textContent =
`${remaining} task${remaining !== 1 ? 's' : ''} remaining`;
}
}
new TodoApp();