A small task manager that keeps your tasks in a stack and shows one thing: the task you are doing now, inside a 25-minute tomato. It runs in the browser with plain HTML, CSS and JavaScript: no build step, no dependencies.
- One thing now: the top of the stack fills the first screen: its name, a tomato dial with the time left, and two big buttons, Start and Done. Under it a single line says what comes Then (tap it to jump to the waiting list). A new task goes on top. The task count is kept for screen readers rather than shown as a number.
- The tomato: one tomato is 25 minutes, fixed. Start counts the time left; the red wedge shrinks, the tab title shows the clock, and under five minutes the tomato tilts. At 25:00 the timer stops (it never completes the task), the clock shows 00:00 and the tab title says "Ripe!": take five, then Done, or press One more. A tomato that ripens while the tab is in the background (where browsers slow down or freeze timers) is still recorded at exactly 25 minutes, and a toast tells you when you are back. The time left is derived from the time already logged on the task, so older data with a
timeSpentshows up as ripe tomatoes and time left. A timer left running while the page was closed (for example by the earlier count-up version) is stopped when you come back, every logged minute is kept, and the task shows as ripe until you start it again. - Timer rule: a timer belongs to the top task, and a running timer is always on the first screen. When something else becomes the top (you add a task, move one, or undo), the timer of the task that got buried is paused. Switching to another category (a tab, the arrow keys, or a new category) pauses it too, with a toast; its time is kept, and Start carries on when you come back. If its tomato was already up, it is recorded as ripe at 25:00 instead. When the app opens (or another tab or an import changes the data) with a timer running in another category, that category is opened and the timer keeps running.
- Complete: Done moves the task to the Completed list and the next one slides in. When the plate is empty you see how many you finished today and the tomatoes they ripened. Tasks can also be archived, deleted, or moved up and down with the buttons on each row. On a touch screen the row buttons stay folded away until you tap the row (one row open at a time); with a mouse they appear on hover, and with the keyboard on focus. With a mouse, drag a waiting row and the others slide aside to make room.
- Rename: double-click a task (or press
F2from one of its buttons) to edit it in place.Entersaves,Esccancels. - Categories: each category (
WorkandPersonalto start with) has its own stack, shown as tabs. Add, rename and delete them with the buttons at the bottom of the page, or double-click a tab to rename it. Drag tabs, or pressAlt+ arrow keys, to reorder them. - Archive and Completed: collapsible lists per category. Restore a task to the bottom of its stack, or delete it for good.
- Undo: completing, archiving, restoring, deleting a task or a category, and importing show an Undo toast for a few seconds (also
Ctrl/Cmd+Z). Only the latest action can be undone. Toasts sit at the bottom of the screen, clear of the category tabs and the task to do now; the page keeps room under its tools so they stay uncovered. - Keyboard and touch: everything is reachable by keyboard (arrow keys switch tabs,
F2renames), and the layout adapts to phone-sized screens. On desktop the cursor starts in the input, so you can type a task straight away; the dial changes once a second without animation, and animations are switched off when your system asks for reduced motion.
| Waiting | Focused | Just done |
|---|---|---|
![]() |
![]() |
![]() |
More: desktop stack, completed list, empty plate, empty plate on a phone, ripe on a phone, switched away from a running tomato on a phone.
Palette (light only, WCAG AA checked in tests/unit/contrast.test.js): paper #FFF4E6, ink #2A1A14, soft #6E5247, tomato #E0442B (the wedge only), tomato-deep #B42D1A, leaf #1F6B38, blush #FFE1D6, sun #FFC94A.
Open index.html in a browser, or serve the folder:
npm run serve # http://127.0.0.1:4173/ (uses node, nothing to install)The Inter and JetBrains Mono fonts are loaded from Google Fonts when available; without them the system fonts are used.
Everything stays in your browser. There is no server and no account.
- Tasks are stored as JSON in
localStorageunder the keyfileDB:{ tasks, completed, archived, categories, currentCategory, schemaVersion }. A task that has been timed carriestimeSpent(milliseconds) and, while its timer runs,timerStartedAt. - Export downloads that data as
tasks-backup-YYYY-MM-DD.json. Import checks the file first, asks before replacing your current data, and can be undone. Files larger than about 5 MB (5,242,880 characters) or with invalid tasks are rejected without changing anything. Text and category length are not limited on import, so backups from earlier versions import as they are. - Data from earlier versions loads as is, including long tasks and category names (the input limits apply only to new tasks and categories). If only the older separate keys (
taskStack,completedTasks,archivedTasks,categories,currentCategory) exist, they are migrated intofileDB. Those old keys are left in place;fileDBalways takes precedence. - If stored data is unreadable or partly invalid, the original text is first copied to
fileDB.backup-<timestamp>. Readable tasks still load, unusable ones are skipped, and a message says how many. If no backup can be written, the app does not save over the original. - Clearing site data deletes your tasks. Export a backup if they matter.
npm test # unit tests, Node's built-in test runner, no install needed
npm install # once, for the browser tests and screenshots
npx playwright install chromium
npm run test:e2e # smoke tests in headless Chromium, desktop and phone viewport
npm run screenshots # regenerates docs/screenshots/*.pngSource layout: state.js (task and category logic), storage.js (localStorage, import and export), ui.js (toast, live region, dialog), render.js (DOM), dnd.js (drag and drop), app.js (event wiring). They are classic scripts loaded in that order by index.html, so the page also works from file://.
MIT, see LICENSE.



