| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Modern Todo App</title> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| :root { |
| --primary: #4361ee; |
| --secondary: #3f37c9; |
| --accent: #4895ef; |
| --light: #f8f9fa; |
| --dark: #212529; |
| --success: #4cc9f0; |
| --danger: #f72585; |
| --warning: #f8961e; |
| --info: #560bad; |
| --border-radius: 8px; |
| --shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
| --transition: all 0.3s ease; |
| } |
| |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| } |
| |
| body { |
| background-color: #f5f7fa; |
| color: var(--dark); |
| line-height: 1.6; |
| padding: 20px; |
| min-height: 100vh; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| } |
| |
| .container { |
| width: 100%; |
| max-width: 600px; |
| background: white; |
| border-radius: var(--border-radius); |
| box-shadow: var(--shadow); |
| padding: 2rem; |
| margin-top: 2rem; |
| } |
| |
| h1 { |
| color: var(--primary); |
| text-align: center; |
| margin-bottom: 1.5rem; |
| font-weight: 700; |
| font-size: 2.2rem; |
| } |
| |
| .input-container { |
| display: flex; |
| gap: 10px; |
| margin-bottom: 1.5rem; |
| } |
| |
| #task-input { |
| flex: 1; |
| padding: 12px 15px; |
| border: 2px solid #e9ecef; |
| border-radius: var(--border-radius); |
| font-size: 1rem; |
| transition: var(--transition); |
| } |
| |
| #task-input:focus { |
| outline: none; |
| border-color: var(--accent); |
| box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.2); |
| } |
| |
| #add-btn { |
| background-color: var(--primary); |
| color: white; |
| border: none; |
| padding: 12px 20px; |
| border-radius: var(--border-radius); |
| cursor: pointer; |
| font-weight: 600; |
| transition: var(--transition); |
| } |
| |
| #add-btn:hover { |
| background-color: var(--secondary); |
| transform: translateY(-2px); |
| } |
| |
| .task-list { |
| list-style: none; |
| margin-top: 1.5rem; |
| } |
| |
| .task-item { |
| display: flex; |
| align-items: center; |
| padding: 12px 15px; |
| background-color: white; |
| border-radius: var(--border-radius); |
| margin-bottom: 10px; |
| box-shadow: var(--shadow); |
| transition: var(--transition); |
| border-left: 4px solid transparent; |
| } |
| |
| .task-item:hover { |
| transform: translateY(-2px); |
| box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1); |
| } |
| |
| .task-item.completed { |
| opacity: 0.7; |
| border-left-color: var(--success); |
| } |
| |
| .task-item.completed .task-text { |
| text-decoration: line-through; |
| color: #6c757d; |
| } |
| |
| .task-checkbox { |
| margin-right: 15px; |
| width: 20px; |
| height: 20px; |
| cursor: pointer; |
| accent-color: var(--success); |
| } |
| |
| .task-text { |
| flex: 1; |
| font-size: 1rem; |
| word-break: break-word; |
| } |
| |
| .delete-btn { |
| background: none; |
| border: none; |
| color: var(--danger); |
| cursor: pointer; |
| font-size: 1.2rem; |
| margin-left: 10px; |
| transition: var(--transition); |
| opacity: 0.7; |
| } |
| |
| .delete-btn:hover { |
| opacity: 1; |
| transform: scale(1.1); |
| } |
| |
| .stats { |
| display: flex; |
| justify-content: space-between; |
| margin-top: 1.5rem; |
| padding-top: 1rem; |
| border-top: 1px solid #e9ecef; |
| color: #6c757d; |
| font-size: 0.9rem; |
| } |
| |
| .filter-buttons { |
| display: flex; |
| gap: 10px; |
| margin-top: 1rem; |
| justify-content: center; |
| } |
| |
| .filter-btn { |
| padding: 8px 12px; |
| border-radius: var(--border-radius); |
| border: 1px solid #e9ecef; |
| background: white; |
| cursor: pointer; |
| transition: var(--transition); |
| font-size: 0.85rem; |
| } |
| |
| .filter-btn.active { |
| background-color: var(--primary); |
| color: white; |
| border-color: var(--primary); |
| } |
| |
| .filter-btn:hover { |
| background-color: #e9ecef; |
| } |
| |
| .filter-btn.active:hover { |
| background-color: var(--secondary); |
| } |
| |
| .empty-state { |
| text-align: center; |
| padding: 2rem 0; |
| color: #6c757d; |
| } |
| |
| .empty-state i { |
| font-size: 3rem; |
| margin-bottom: 1rem; |
| color: #e9ecef; |
| } |
| |
| @media (max-width: 600px) { |
| .container { |
| padding: 1.5rem; |
| } |
| |
| h1 { |
| font-size: 1.8rem; |
| } |
| |
| .input-container { |
| flex-direction: column; |
| } |
| |
| #add-btn { |
| width: 100%; |
| } |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Todo App</h1> |
| |
| <div class="input-container"> |
| <input type="text" id="task-input" placeholder="Add a new task..." autofocus> |
| <button id="add-btn">Add Task</button> |
| </div> |
| |
| <div class="filter-buttons"> |
| <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="task-list" id="task-list"> |
| |
| <div class="empty-state"> |
| <i class="fas fa-clipboard-list"></i> |
| <p>No tasks yet. Add one above!</p> |
| </div> |
| </ul> |
| |
| <div class="stats"> |
| <span id="total-tasks">0 tasks</span> |
| <span id="completed-tasks">0 completed</span> |
| </div> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| const taskInput = document.getElementById('task-input'); |
| const addBtn = document.getElementById('add-btn'); |
| const taskList = document.getElementById('task-list'); |
| const totalTasksSpan = document.getElementById('total-tasks'); |
| const completedTasksSpan = document.getElementById('completed-tasks'); |
| const filterButtons = document.querySelectorAll('.filter-btn'); |
| |
| let tasks = JSON.parse(localStorage.getItem('tasks')) || []; |
| let currentFilter = 'all'; |
| |
| |
| function renderTasks() { |
| taskList.innerHTML = ''; |
| |
| const filteredTasks = tasks.filter(task => { |
| if (currentFilter === 'all') return true; |
| if (currentFilter === 'active') return !task.completed; |
| if (currentFilter === 'completed') return task.completed; |
| }); |
| |
| if (filteredTasks.length === 0) { |
| taskList.innerHTML = ` |
| <div class="empty-state"> |
| <i class="fas fa-clipboard-list"></i> |
| <p>No ${currentFilter} tasks found.</p> |
| </div> |
| `; |
| return; |
| } |
| |
| filteredTasks.forEach((task, index) => { |
| const taskItem = document.createElement('li'); |
| taskItem.className = `task-item ${task.completed ? 'completed' : ''}`; |
| taskItem.innerHTML = ` |
| <input type="checkbox" class="task-checkbox" ${task.completed ? 'checked' : ''} data-id="${task.id}"> |
| <span class="task-text">${task.text}</span> |
| <button class="delete-btn" data-id="${task.id}"> |
| <i class="fas fa-trash"></i> |
| </button> |
| `; |
| taskList.appendChild(taskItem); |
| }); |
| |
| updateStats(); |
| } |
| |
| |
| function updateStats() { |
| const totalTasks = tasks.length; |
| const completedTasks = tasks.filter(task => task.completed).length; |
| |
| totalTasksSpan.textContent = `${totalTasks} ${totalTasks === 1 ? 'task' : 'tasks'}`; |
| completedTasksSpan.textContent = `${completedTasks} completed`; |
| } |
| |
| |
| function addTask() { |
| const text = taskInput.value.trim(); |
| if (text === '') return; |
| |
| const newTask = { |
| id: Date.now(), |
| text, |
| completed: false |
| }; |
| |
| tasks.push(newTask); |
| saveTasks(); |
| taskInput.value = ''; |
| renderTasks(); |
| } |
| |
| |
| function toggleTask(id) { |
| tasks = tasks.map(task => { |
| if (task.id === id) { |
| return { ...task, completed: !task.completed }; |
| } |
| return task; |
| }); |
| saveTasks(); |
| renderTasks(); |
| } |
| |
| |
| function deleteTask(id) { |
| tasks = tasks.filter(task => task.id !== id); |
| saveTasks(); |
| renderTasks(); |
| } |
| |
| |
| function saveTasks() { |
| localStorage.setItem('tasks', JSON.stringify(tasks)); |
| } |
| |
| |
| addBtn.addEventListener('click', addTask); |
| |
| taskInput.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') { |
| addTask(); |
| } |
| }); |
| |
| taskList.addEventListener('click', function(e) { |
| if (e.target.classList.contains('task-checkbox')) { |
| const id = parseInt(e.target.getAttribute('data-id')); |
| toggleTask(id); |
| } |
| |
| if (e.target.classList.contains('delete-btn') || e.target.closest('.delete-btn')) { |
| const id = parseInt(e.target.closest('.delete-btn').getAttribute('data-id')); |
| deleteTask(id); |
| } |
| }); |
| |
| filterButtons.forEach(button => { |
| button.addEventListener('click', function() { |
| filterButtons.forEach(btn => btn.classList.remove('active')); |
| this.classList.add('active'); |
| currentFilter = this.getAttribute('data-filter'); |
| renderTasks(); |
| }); |
| }); |
| |
| |
| renderTasks(); |
| }); |
| </script> |
| </body> |
| </html> |