anycoder-641c2fbf / index.html
akhaliq's picture
akhaliq HF Staff
Upload folder using huggingface_hub
d38d101 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Taskflow - Modern Todo App</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0a0a0f;
--bg-secondary: #12121a;
--fg: #f0f0f5;
--muted: #6b6b80;
--accent: #00d4aa;
--accent-glow: rgba(0, 212, 170, 0.3);
--card: rgba(20, 20, 30, 0.8);
--border: rgba(255, 255, 255, 0.08);
--danger: #ff4757;
--danger-glow: rgba(255, 71, 87, 0.3);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: 'Inter', sans-serif;
background: var(--bg);
color: var(--fg);
min-height: 100vh;
overflow-x: hidden;
}
.font-display {
font-family: 'Space Grotesk', sans-serif;
}
/* Animated background */
.bg-mesh {
position: fixed;
inset: 0;
z-index: -1;
overflow: hidden;
}
.bg-mesh::before {
content: '';
position: absolute;
width: 150%;
height: 150%;
top: -25%;
left: -25%;
background:
radial-gradient(ellipse 600px 600px at 20% 20%, rgba(0, 212, 170, 0.08) 0%, transparent 50%),
radial-gradient(ellipse 500px 500px at 80% 80%, rgba(100, 50, 200, 0.06) 0%, transparent 50%),
radial-gradient(ellipse 400px 400px at 60% 30%, rgba(0, 150, 255, 0.05) 0%, transparent 50%);
animation: meshMove 20s ease-in-out infinite;
}
@keyframes meshMove {
0%, 100% { transform: translate(0, 0) rotate(0deg); }
33% { transform: translate(2%, 2%) rotate(1deg); }
66% { transform: translate(-1%, 1%) rotate(-1deg); }
}
/* Grid pattern overlay */
.grid-pattern {
position: fixed;
inset: 0;
z-index: -1;
background-image:
linear-gradient(rgba(255, 255, 255, 0.02) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.02) 1px, transparent 1px);
background-size: 60px 60px;
mask-image: radial-gradient(ellipse 80% 80% at 50% 50%, black 20%, transparent 70%);
}
/* Glass card effect */
.glass-card {
background: var(--card);
backdrop-filter: blur(20px);
border: 1px solid var(--border);
border-radius: 20px;
}
/* Custom scrollbar */
::-webkit-scrollbar {
width: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--border);
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.15);
}
/* Input styling */
.todo-input {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border);
border-radius: 14px;
padding: 16px 20px;
font-size: 16px;
color: var(--fg);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
width: 100%;
}
.todo-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 4px var(--accent-glow);
background: rgba(255, 255, 255, 0.05);
}
.todo-input::placeholder {
color: var(--muted);
}
/* Button styling */
.btn-primary {
background: var(--accent);
color: var(--bg);
font-weight: 600;
padding: 16px 28px;
border-radius: 14px;
border: none;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-size: 15px;
display: flex;
align-items: center;
gap: 8px;
white-space: nowrap;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 30px var(--accent-glow);
}
.btn-primary:active {
transform: translateY(0);
}
/* Filter buttons */
.filter-btn {
background: transparent;
border: 1px solid var(--border);
color: var(--muted);
padding: 10px 18px;
border-radius: 10px;
cursor: pointer;
transition: all 0.25s ease;
font-size: 14px;
font-weight: 500;
}
.filter-btn:hover {
border-color: rgba(255, 255, 255, 0.15);
color: var(--fg);
}
.filter-btn.active {
background: var(--accent);
border-color: var(--accent);
color: var(--bg);
}
/* Todo item */
.todo-item {
background: rgba(255, 255, 255, 0.02);
border: 1px solid var(--border);
border-radius: 14px;
padding: 18px 20px;
display: flex;
align-items: center;
gap: 16px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
animation: slideIn 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.todo-item:hover {
background: rgba(255, 255, 255, 0.04);
border-color: rgba(255, 255, 255, 0.12);
transform: translateX(4px);
}
.todo-item.completed {
opacity: 0.5;
}
.todo-item.completed .todo-text {
text-decoration: line-through;
color: var(--muted);
}
.todo-item.removing {
animation: slideOut 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slideOut {
to {
opacity: 0;
transform: translateX(30px);
}
}
/* Custom checkbox */
.custom-checkbox {
width: 24px;
height: 24px;
border: 2px solid var(--border);
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.25s ease;
flex-shrink: 0;
}
.custom-checkbox:hover {
border-color: var(--accent);
}
.custom-checkbox.checked {
background: var(--accent);
border-color: var(--accent);
}
.custom-checkbox svg {
opacity: 0;
transform: scale(0.5);
transition: all 0.2s ease;
}
.custom-checkbox.checked svg {
opacity: 1;
transform: scale(1);
}
/* Delete button */
.delete-btn {
background: transparent;
border: none;
color: var(--muted);
cursor: pointer;
padding: 8px;
border-radius: 8px;
transition: all 0.25s ease;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
}
.todo-item:hover .delete-btn {
opacity: 1;
}
.delete-btn:hover {
background: var(--danger-glow);
color: var(--danger);
}
/* Progress bar */
.progress-bar {
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--accent), #00ffcc);
border-radius: 3px;
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}
/* Empty state */
.empty-state {
text-align: center;
padding: 60px 20px;
color: var(--muted);
}
.empty-state svg {
margin-bottom: 20px;
opacity: 0.5;
}
/* Stats card */
.stat-card {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border);
border-radius: 12px;
padding: 16px 20px;
text-align: center;
}
.stat-value {
font-size: 28px;
font-weight: 700;
color: var(--accent);
line-height: 1;
}
.stat-label {
font-size: 12px;
color: var(--muted);
margin-top: 4px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Header link */
.header-link {
color: var(--muted);
text-decoration: none;
font-size: 12px;
transition: color 0.2s ease;
}
.header-link:hover {
color: var(--accent);
}
/* Entrance animations */
.fade-up {
opacity: 0;
transform: translateY(20px);
animation: fadeUp 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
}
.fade-up:nth-child(1) { animation-delay: 0.1s; }
.fade-up:nth-child(2) { animation-delay: 0.2s; }
.fade-up:nth-child(3) { animation-delay: 0.3s; }
@keyframes fadeUp {
to {
opacity: 1;
transform: translateY(0);
}
}
/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Focus visible */
:focus-visible {
outline: 2px solid var(--accent);
outline-offset: 2px;
}
button:focus:not(:focus-visible) {
outline: none;
}
</style>
</head>
<body>
<div class="bg-mesh"></div>
<div class="grid-pattern"></div>
<div class="min-h-screen py-8 px-4 sm:px-6 lg:px-8">
<div class="max-w-2xl mx-auto">
<!-- Header -->
<header class="text-center mb-10 fade-up">
<div class="flex items-center justify-between mb-4">
<span></span>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" rel="noopener" class="header-link">
Built with anycoder
</a>
</div>
<h1 class="font-display text-4xl sm:text-5xl font-bold tracking-tight mb-3">
Task<span style="color: var(--accent)">flow</span>
</h1>
<p class="text-base" style="color: var(--muted)">Organize your day, one task at a time</p>
</header>
<!-- Main Card -->
<main class="glass-card p-6 sm:p-8 fade-up" style="animation-delay: 0.15s;">
<!-- Input Section -->
<form id="todo-form" class="flex flex-col sm:flex-row gap-3 mb-8">
<input
type="text"
id="todo-input"
class="todo-input flex-1"
placeholder="What needs to be done?"
aria-label="New todo input"
autocomplete="off"
>
<button type="submit" class="btn-primary justify-center">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
Add Task
</button>
</form>
<!-- Progress Section -->
<div class="mb-6">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-medium" style="color: var(--muted)">Progress</span>
<span id="progress-text" class="text-sm font-semibold" style="color: var(--accent)">0%</span>
</div>
<div class="progress-bar">
<div id="progress-fill" class="progress-fill" style="width: 0%"></div>
</div>
</div>
<!-- Stats -->
<div class="grid grid-cols-3 gap-3 mb-6">
<div class="stat-card">
<div id="stat-total" class="stat-value">0</div>
<div class="stat-label">Total</div>
</div>
<div class="stat-card">
<div id="stat-active" class="stat-value">0</div>
<div class="stat-label">Active</div>
</div>
<div class="stat-card">
<div id="stat-completed" class="stat-value">0</div>
<div class="stat-label">Done</div>
</div>
</div>
<!-- Filters -->
<div class="flex flex-wrap gap-2 mb-6">
<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>
<button id="clear-completed" class="filter-btn ml-auto" style="color: var(--danger); border-color: rgba(255, 71, 87, 0.3);">
Clear Done
</button>
</div>
<!-- Todo List -->
<div id="todo-list" class="space-y-3" role="list" aria-label="Todo list">
<!-- Todos will be rendered here -->
</div>
<!-- Empty State -->
<div id="empty-state" class="empty-state">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 11l3 3L22 4"></path>
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path>
</svg>
<p class="font-display text-lg font-medium mb-1">No tasks yet</p>
<p class="text-sm">Add your first task to get started</p>
</div>
</main>
<!-- Footer -->
<footer class="text-center mt-8 fade-up" style="animation-delay: 0.25s;">
<p class="text-xs" style="color: var(--muted)">
Press Enter to add task. Click to complete. Data saved locally.
</p>
</footer>
</div>
</div>
<script>
// Initialize state
let todos = JSON.parse(localStorage.getItem('taskflow-todos')) || [];
let currentFilter = 'all';
// DOM Elements
const todoForm = document.getElementById('todo-form');
const todoInput = document.getElementById('todo-input');
const todoList = document.getElementById('todo-list');
const emptyState = document.getElementById('empty-state');
const filterBtns = document.querySelectorAll('.filter-btn[data-filter]');
const clearCompletedBtn = document.getElementById('clear-completed');
const progressFill = document.getElementById('progress-fill');
const progressText = document.getElementById('progress-text');
const statTotal = document.getElementById('stat-total');
const statActive = document.getElementById('stat-active');
const statCompleted = document.getElementById('stat-completed');
// Generate unique ID
function generateId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
// Save to localStorage
function saveTodos() {
localStorage.setItem('taskflow-todos', JSON.stringify(todos));
}
// Update stats
function updateStats() {
const total = todos.length;
const completed = todos.filter(t => t.completed).length;
const active = total - completed;
const progress = total > 0 ? Math.round((completed / total) * 100) : 0;
statTotal.textContent = total;
statActive.textContent = active;
statCompleted.textContent = completed;
progressFill.style.width = `${progress}%`;
progressText.textContent = `${progress}%`;
}
// Create todo element
function createTodoElement(todo) {
const item = document.createElement('div');
item.className = `todo-item ${todo.completed ? 'completed' : ''}`;
item.dataset.id = todo.id;
item.setAttribute('role', 'listitem');
item.innerHTML = `
<div class="custom-checkbox ${todo.completed ? 'checked' : ''}" role="checkbox" aria-checked="${todo.completed}" tabindex="0" aria-label="Mark as ${todo.completed ? 'incomplete' : 'complete'}">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#0a0a0f" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
</div>
<span class="todo-text flex-1">${escapeHtml(todo.text)}</span>
<button class="delete-btn" aria-label="Delete task">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
</svg>
</button>
`;
// Toggle complete
const checkbox = item.querySelector('.custom-checkbox');
checkbox.addEventListener('click', () => toggleTodo(todo.id));
checkbox.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
toggleTodo(todo.id);
}
});
// Delete
const deleteBtn = item.querySelector('.delete-btn');
deleteBtn.addEventListener('click', () => deleteTodo(todo.id));
return item;
}
// Escape HTML
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Render todos
function renderTodos() {
const filteredTodos = todos.filter(todo => {
if (currentFilter === 'active') return !todo.completed;
if (currentFilter === 'completed') return todo.completed;
return true;
});
todoList.innerHTML = '';
if (filteredTodos.length === 0) {
emptyState.style.display = 'block';
todoList.style.display = 'none';
} else {
emptyState.style.display = 'none';
todoList.style.display = 'flex';
todoList.style.flexDirection = 'column';
filteredTodos.forEach(todo => {
todoList.appendChild(createTodoElement(todo));
});
}
updateStats();
}
// Add todo
function addTodo(text) {
const trimmedText = text.trim();
if (!trimmedText) return;
const todo = {
id: generateId(),
text: trimmedText,
completed: false,
createdAt: Date.now()
};
todos.unshift(todo);
saveTodos();
renderTodos();
todoInput.value = '';
}
// Toggle todo
function toggleTodo(id) {
todos = todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
);
saveTodos();
renderTodos();
}
// Delete todo
function deleteTodo(id) {
const item = todoList.querySelector(`[data-id="${id}"]`);
if (item) {
item.classList.add('removing');
setTimeout(() => {
todos = todos.filter(todo => todo.id !== id);
saveTodos();
renderTodos();
}, 300);
}
}
// Clear completed
function clearCompleted() {
const completedItems = todoList.querySelectorAll('.todo-item.completed');
completedItems.forEach(item => item.classList.add('removing'));
setTimeout(() => {
todos = todos.filter(todo => !todo.completed);
saveTodos();
renderTodos();
}, 300);
}
// Event Listeners
todoForm.addEventListener('submit', (e) => {
e.preventDefault();
addTodo(todoInput.value);
});
filterBtns.forEach(btn => {
btn.addEventListener('click', () => {
filterBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
currentFilter = btn.dataset.filter;
renderTodos();
});
});
clearCompletedBtn.addEventListener('click', clearCompleted);
// Initial render
renderTodos();
</script>
</body>
</html>