Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Reminders</title> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | |
| <style> | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif; | |
| min-height: 100vh; | |
| background: linear-gradient(180deg, #1c1c1e 0%, #2c2c2e 100%); | |
| display: flex; | |
| justify-content: center; | |
| align-items: flex-start; | |
| padding: 40px 20px; | |
| } | |
| .container { | |
| background: rgba(44, 44, 46, 0.8); | |
| backdrop-filter: blur(40px); | |
| -webkit-backdrop-filter: blur(40px); | |
| border-radius: 20px; | |
| padding: 0; | |
| width: 100%; | |
| max-width: 420px; | |
| box-shadow: 0 25px 80px rgba(0, 0, 0, 0.5), | |
| inset 0 0 0 0.5px rgba(255, 255, 255, 0.1); | |
| overflow: hidden; | |
| } | |
| header { | |
| padding: 24px 24px 20px; | |
| background: linear-gradient(180deg, rgba(255,255,255,0.08) 0%, transparent 100%); | |
| } | |
| header a { | |
| font-size: 11px; | |
| color: rgba(255, 255, 255, 0.4); | |
| text-decoration: none; | |
| letter-spacing: 0.3px; | |
| display: block; | |
| margin-bottom: 8px; | |
| } | |
| header a:hover { | |
| color: rgba(255, 255, 255, 0.6); | |
| } | |
| h1 { | |
| color: #fff; | |
| font-size: 34px; | |
| font-weight: 700; | |
| letter-spacing: -0.5px; | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| h1 i { | |
| color: #0a84ff; | |
| font-size: 28px; | |
| } | |
| .input-group { | |
| display: flex; | |
| gap: 12px; | |
| padding: 0 20px 20px; | |
| } | |
| input[type="text"] { | |
| flex: 1; | |
| padding: 14px 18px; | |
| background: rgba(118, 118, 128, 0.24); | |
| border: none; | |
| border-radius: 12px; | |
| font-size: 17px; | |
| color: #fff; | |
| font-family: inherit; | |
| transition: all 0.2s ease; | |
| } | |
| input[type="text"]::placeholder { | |
| color: rgba(235, 235, 245, 0.3); | |
| } | |
| input[type="text"]:focus { | |
| outline: none; | |
| background: rgba(118, 118, 128, 0.36); | |
| box-shadow: 0 0 0 4px rgba(10, 132, 255, 0.3); | |
| } | |
| .add-btn { | |
| width: 50px; | |
| height: 50px; | |
| background: #0a84ff; | |
| color: white; | |
| border: none; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| font-size: 20px; | |
| transition: all 0.2s ease; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .add-btn:hover { | |
| background: #409cff; | |
| transform: scale(1.05); | |
| } | |
| .add-btn:active { | |
| transform: scale(0.95); | |
| } | |
| .todo-list { | |
| list-style: none; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| padding: 0 8px; | |
| } | |
| .todo-list::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| .todo-list::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| .todo-list::-webkit-scrollbar-thumb { | |
| background: rgba(255, 255, 255, 0.2); | |
| border-radius: 3px; | |
| } | |
| .todo-item { | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| padding: 16px; | |
| margin: 0 12px 2px; | |
| background: transparent; | |
| border-radius: 12px; | |
| transition: all 0.2s ease; | |
| animation: fadeIn 0.3s ease; | |
| border-bottom: 0.5px solid rgba(84, 84, 88, 0.65); | |
| } | |
| .todo-item:last-of-type { | |
| border-bottom: none; | |
| } | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(-10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .todo-item:hover { | |
| background: rgba(255, 255, 255, 0.05); | |
| } | |
| .todo-item.completed span { | |
| text-decoration: line-through; | |
| color: rgba(235, 235, 245, 0.3); | |
| } | |
| .checkbox-wrapper { | |
| position: relative; | |
| width: 24px; | |
| height: 24px; | |
| } | |
| .todo-item input[type="checkbox"] { | |
| appearance: none; | |
| -webkit-appearance: none; | |
| width: 24px; | |
| height: 24px; | |
| border: 2px solid rgba(142, 142, 147, 0.6); | |
| border-radius: 50%; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| background: transparent; | |
| } | |
| .todo-item input[type="checkbox"]:checked { | |
| background: #30d158; | |
| border-color: #30d158; | |
| } | |
| .todo-item input[type="checkbox"]:checked::after { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -60%) rotate(45deg); | |
| width: 6px; | |
| height: 10px; | |
| border: solid white; | |
| border-width: 0 2px 2px 0; | |
| } | |
| .todo-item span { | |
| flex: 1; | |
| font-size: 17px; | |
| color: #fff; | |
| word-break: break-word; | |
| line-height: 1.4; | |
| letter-spacing: -0.2px; | |
| } | |
| .delete-btn { | |
| background: transparent; | |
| padding: 8px; | |
| border-radius: 8px; | |
| font-size: 16px; | |
| color: rgba(235, 235, 245, 0.3); | |
| opacity: 0; | |
| transition: all 0.2s ease; | |
| } | |
| .todo-item:hover .delete-btn { | |
| opacity: 1; | |
| } | |
| .delete-btn:hover { | |
| color: #ff453a; | |
| background: rgba(255, 69, 58, 0.15); | |
| transform: none; | |
| box-shadow: none; | |
| } | |
| .empty { | |
| text-align: center; | |
| color: rgba(235, 235, 245, 0.3); | |
| padding: 60px 40px; | |
| font-size: 17px; | |
| line-height: 1.6; | |
| } | |
| .empty i { | |
| font-size: 48px; | |
| margin-bottom: 16px; | |
| display: block; | |
| color: rgba(235, 235, 245, 0.2); | |
| } | |
| .stats { | |
| display: flex; | |
| justify-content: space-between; | |
| padding: 20px 24px; | |
| background: rgba(0, 0, 0, 0.2); | |
| color: rgba(235, 235, 245, 0.6); | |
| font-size: 13px; | |
| font-weight: 500; | |
| letter-spacing: 0.2px; | |
| } | |
| .stats span { | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| } | |
| .stats .count { | |
| background: rgba(118, 118, 128, 0.24); | |
| padding: 4px 10px; | |
| border-radius: 20px; | |
| font-weight: 600; | |
| color: #fff; | |
| } | |
| .completed-count .count { | |
| background: rgba(48, 209, 88, 0.2); | |
| color: #30d158; | |
| } | |
| /* Categories/Tags Section */ | |
| .categories { | |
| display: flex; | |
| gap: 8px; | |
| padding: 0 20px 16px; | |
| overflow-x: auto; | |
| } | |
| .category { | |
| padding: 8px 16px; | |
| background: rgba(118, 118, 128, 0.24); | |
| border-radius: 20px; | |
| font-size: 14px; | |
| color: rgba(235, 235, 245, 0.6); | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| white-space: nowrap; | |
| border: none; | |
| } | |
| .category.active { | |
| background: #0a84ff; | |
| color: #fff; | |
| } | |
| .category:hover:not(.active) { | |
| background: rgba(118, 118, 128, 0.36); | |
| } | |
| @media (max-width: 480px) { | |
| body { | |
| padding: 0; | |
| align-items: flex-start; | |
| } | |
| .container { | |
| border-radius: 0; | |
| min-height: 100vh; | |
| max-width: 100%; | |
| } | |
| h1 { | |
| font-size: 28px; | |
| } | |
| .todo-list { | |
| max-height: calc(100vh - 320px); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">Built with anycoder</a> | |
| <h1><i class="fas fa-list-check"></i> Reminders</h1> | |
| </header> | |
| <div class="categories"> | |
| <button class="category active" onclick="filterTodos('all')">All</button> | |
| <button class="category" onclick="filterTodos('active')">Active</button> | |
| <button class="category" onclick="filterTodos('completed')">Completed</button> | |
| </div> | |
| <div class="input-group"> | |
| <input type="text" id="todoInput" placeholder="Add a new reminder..." onkeypress="if(event.key==='Enter')addTodo()"> | |
| <button class="add-btn" onclick="addTodo()"><i class="fas fa-plus"></i></button> | |
| </div> | |
| <ul class="todo-list" id="todoList"></ul> | |
| <div class="stats"> | |
| <span id="total">Total <span class="count">0</span></span> | |
| <span id="completed" class="completed-count">Done <span class="count">0</span></span> | |
| </div> | |
| </div> | |
| <script> | |
| let todos = JSON.parse(localStorage.getItem('todos')) || []; | |
| let currentFilter = 'all'; | |
| function saveTodos() { | |
| localStorage.setItem('todos', JSON.stringify(todos)); | |
| } | |
| function filterTodos(filter) { | |
| currentFilter = filter; | |
| document.querySelectorAll('.category').forEach(btn => btn.classList.remove('active')); | |
| event.target.classList.add('active'); | |
| renderTodos(); | |
| } | |
| function getFilteredTodos() { | |
| switch(currentFilter) { | |
| case 'active': | |
| return todos.filter(t => !t.done); | |
| case 'completed': | |
| return todos.filter(t => t.done); | |
| default: | |
| return todos; | |
| } | |
| } | |
| function renderTodos() { | |
| const list = document.getElementById('todoList'); | |
| const filteredTodos = getFilteredTodos(); | |
| if (filteredTodos.length === 0) { | |
| let emptyMessage = 'No reminders yet!'; | |
| if (currentFilter === 'active') emptyMessage = 'No active reminders'; | |
| if (currentFilter === 'completed') emptyMessage = 'No completed reminders'; | |
| list.innerHTML = `<li class="empty"><i class="fas fa-bell-slash"></i>${emptyMessage}</li>`; | |
| } else { | |
| list.innerHTML = ''; | |
| filteredTodos.forEach((todo) => { | |
| const originalIndex = todos.indexOf(todo); | |
| list.innerHTML += ` | |
| <li class="todo-item ${todo.done ? 'completed' : ''}"> | |
| <div class="checkbox-wrapper"> | |
| <input type="checkbox" ${todo.done ? 'checked' : ''} onchange="toggleTodo(${originalIndex})"> | |
| </div> | |
| <span>${escapeHtml(todo.text)}</span> | |
| <button class="delete-btn" onclick="deleteTodo(${originalIndex})"> | |
| <i class="fas fa-xmark"></i> | |
| </button> | |
| </li>`; | |
| }); | |
| } | |
| document.getElementById('total').innerHTML = `Total <span class="count">${todos.length}</span>`; | |
| document.getElementById('completed').innerHTML = `Done <span class="count">${todos.filter(t => t.done).length}</span>`; | |
| } | |
| function escapeHtml(text) { | |
| const div = document.createElement('div'); | |
| div.textContent = text; | |
| return div.innerHTML; | |
| } | |
| function addTodo() { | |
| const input = document.getElementById('todoInput'); | |
| const text = input.value.trim(); | |
| if (text) { | |
| todos.unshift({ text, done: false, createdAt: Date.now() }); | |
| input.value = ''; | |
| saveTodos(); | |
| renderTodos(); | |
| } | |
| } | |
| function toggleTodo(i) { | |
| todos[i].done = !todos[i].done; | |
| saveTodos(); | |
| renderTodos(); | |
| } | |
| function deleteTodo(i) { | |
| todos.splice(i, 1); | |
| saveTodos(); | |
| renderTodos(); | |
| } | |
| renderTodos(); | |
| </script> | |
| </body> | |
| </html> |