βββββββββ ββββββ βββββββββββ ββββββββββ
ββββββββββββββββββββββββββββ ββββββββββββ
βββ βββββββββββββββββββββββ ββββββββ
βββ βββββββββββββββββββββββ ββββββββ
βββ βββ ββββββββββββββ ββββββ βββ
βββ βββ ββββββββββββββ ββββββ βββ
A minimal, beautiful, production-grade to-do app. Stay organized. Ship faster. Zero friction.
Use Screely for a beautiful browser-frame mockup.
- β¨ Features
- π Tech Stack
- π Project Structure
- π Getting Started
- π Deployment
- π§ Architecture Decisions
- πΈ Screenshots
- π€ Contributing
- π License
Taskr was built in three deliberate phases β from core utility to polished experience.
| Feature | Details |
|---|---|
| β Add Tasks | Type + press Enter or click Add |
| β Complete Tasks | Click the circle β smooth toggle animation |
| π Delete Tasks | Hover a card, hit the trash icon |
| πΎ Persistence | LocalStorage β survives refresh, tab close, everything |
| Feature | Details |
|---|---|
| βοΈ Inline Edit | Hover any task β pencil β type β Enter to save, Esc to cancel |
| π· 4 Categories | Work πΌ Β· Personal π Β· Study π Β· Health πͺ |
| π Due Dates | Smart labels: Today, Tomorrow, 2d overdue |
| π Live Search | Debounced real-time filtering as you type |
| π Filter Tabs | Filter by status (All / Active / Completed) + by category |
| Feature | Details |
|---|---|
| π± Drag & Drop | Reorder tasks by dragging β order persists to localStorage |
| π Dark Mode | Toggle with memory β remembers your preference |
| π Progress Bar | Live completion percentage with smooth animation |
| π Toast Alerts | Contextual notifications for every action |
| π§Ή Clear Done | Bulk-delete all completed tasks in one click |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Layer β Technology β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Markup β HTML5 (semantic, accessible) β
β Styling β Tailwind CSS (CDN) + Custom CSS β
β Logic β Vanilla JS β ES6+, no frameworks β
β Fonts β Fraunces + Plus Jakarta Sans β
β Storage β localStorage (swappable) β
β Deployment β GitHub Pages β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Why vanilla JS? No build step. No npm install. No dependency hell.
Open index.html β it works. That's the whole point.
taskr/
β
βββ π index.html β App shell, all HTML markup & layout
β
βββ π assets/
β β
β βββ π css/
β β βββ style.css β Custom CSS (animations, drag states, fonts)
β β
β βββ π js/
β βββ utils.js β Pure helper functions (no side effects)
β β generateId, formatDate, isOverdue, debounce...
β β
β βββ storage.js β All localStorage CRUD logic
β β Isolated: swap to Firebase by editing 1 file
β β
β βββ ui.js β DOM rendering β the "view" layer
β β createTaskCard, renderTasks, showToast...
β β
β βββ app.js β Orchestrator β state, events, business logic
β State β filter β render cycle lives here
β
βββ π .gitignore
βββ π CONTRIBUTING.md
βββ π LICENSE
βββ π README.md
utils.js β storage.js β ui.js β app.js
β β β β
helpers uses Utils uses Utils uses all
+ Storage
Scripts are loaded in this exact order in index.html. No module bundler needed.
All you need is a browser. Seriously.
# Optional: VS Code Live Server extension for hot-reload dev experience
# Extension ID: ritwickdey.LiveServer# 1. Clone the repo
git clone https://github.com/YOUR-USERNAME/taskr.git
# 2. Navigate into it
cd taskr
# 3. Open in browser
# Option A: Just double-click index.html
# Option B: VS Code β right-click index.html β "Open with Live Server"
# Option C:
npx serve .That's it. No npm install. No build step. No config.
Full step-by-step in
DEPLOYMENT.md
Quick version:
# 1. Push your code to GitHub
git add .
git commit -m "feat: initial Taskr deployment"
git push origin main
# 2. On GitHub β your repo β Settings β Pages
# Source: Deploy from branch
# Branch: main /root
# Save β wait 60 seconds
# 3. Your app is live at:
# https://YOUR-USERNAME.github.io/taskr| Platform | Deploy Method | Time |
|---|---|---|
| Netlify | Drag & drop folder at app.netlify.com/drop | 10s |
| Vercel | npm i -g vercel && vercel in project root |
30s |
| Surge | npm i -g surge && surge |
30s |
These are real patterns used in production apps β worth understanding.
Each JS file has one job and doesn't cross boundaries:
utils.jsnever touches the DOMstorage.jsnever knows about UIui.jsnever calls localStorage directlyapp.jsorchestrates but doesn't do the work itself
const Storage = (() => {
const STORAGE_KEY = 'taskr_v1_tasks'; // Private β not accessible globally
// ...
return { getTasks, addTask, updateTask, deleteTask }; // Public API
})();STORAGE_KEY is private. Changing storage backends later only requires editing this one file β nothing else breaks.
// State lives in ONE place
const state = { filter: 'all', category: 'all', search: '' };
// getFilteredTasks() derives what to show from state + storage
// render() calls both and syncs to DOM
// Every user action follows this pattern:
userAction β mutate state or storage β render()This is fundamentally how React, Vue, and Svelte work.
searchInput.addEventListener('input', Utils.debounce((e) => {
state.search = e.target.value;
render();
}, 200));Without debounce, typing "hello" fires render() 5 times.
With it: fires once, 200ms after you stop typing. Real performance pattern.
Contributions are welcome! See CONTRIBUTING.md for guidelines.
This project is licensed under the MIT License β see LICENSE for details.
Built with β€οΈ by Rohan Kumar
β Star this repo if it helped you! It genuinely motivates continued development.