A modern, responsive todo list app built with vanilla HTML, CSS, and JavaScript. No frameworks, no build step required for the source — just a static site served by Vite.
- Add a new todo by pressing Enter or clicking the + button
- Toggle todos as complete / active with a custom circular checkbox
- Delete individual todos (smooth slide-out animation)
- Filter by All / Active / Completed
- "Items left" counter
- Clear Completed button
- Drag and drop to reorder using the native HTML Drag API
- Persists todos and theme preference in
localStorage
- Optional due date and time picker on each new todo
- Optional "Remind me" toggle — when checked, the app will notify you at the due time
- Reminders use the browser's native Notification API when permission is granted, with an in-app toast as a fallback
- Due-date pills change color automatically:
- Gray for upcoming
- Orange when due within 24 hours
- Red when overdue
- Light and dark modes with a smooth color transition
- Theme toggle in the header — preference is saved per browser
- Follows your system's
prefers-color-schemeon first load
- Gradient header (blue → purple → pink) with soft glowing accents
- Floating card-style container
- Hover effects on buttons and list items
- Fully responsive (mobile + desktop)
- Subtle entrance animation when adding todos
- Toast notifications for reminders
artifacts/todo-app/
├── index.html Semantic HTML, no inline JS
├── style.css CSS variables for theming, smooth transitions
├── script.js Modular vanilla JS using event delegation
├── README.md This file
├── package.json Vite dev/preview scripts
├── tsconfig.json
└── vite.config.ts
The source is just index.html, style.css, and script.js — Vite is only used as a dev server and to produce a static build for deployment.
This app is part of a pnpm workspace. From the repo root:
pnpm install
pnpm --filter @workspace/todo-app run devThen open the URL printed by Vite.
To produce a static build:
pnpm --filter @workspace/todo-app run buildThe built site is written to todo-app/dist/public/.
Todos are stored in localStorage under todo-app:todos as a JSON array:
{
id: string; // generated unique id
text: string; // todo content
completed: boolean; // checked / unchecked
dueAt: string | null; // ISO timestamp, optional
remind: boolean; // whether to fire a reminder at dueAt
notified: boolean; // internal flag — prevents duplicate reminders
}The active theme is stored under todo-app:theme as "light" or "dark".
- Reminders only fire while the app is open in a browser tab. There is no service worker / background sync.
- The first time you enable Remind me, your browser will prompt for notification permission. If you deny it, reminders still appear as in-app toasts.
- Reminders are checked roughly every 20 seconds.
Modern evergreen browsers (Chrome, Edge, Firefox, Safari). Requires support for:
localStorageNotificationAPI (optional — falls back to toasts)- Native HTML Drag and Drop API
prefers-color-scheme