# Simple Todo List — product specification This is the contract the project is built against. It is written for the agent that scaffolds the repository, but it doubles as the human-readable brief: every requirement below is meant to be implementable and verifiable. This is the **minimal demo** template. The goal is the smallest website that is genuinely useful, so the spec is deliberately short and the non-goals matter as much as the goals. Values in angle brackets (``, ``) come from the project-creation dialog and are recorded in the project record (`.w4c/project.json` → `fields`); replace them as you read. ## 1. Goal A single static page that manages a todo list — add a todo, mark it done or undone, delete it, clear the completed ones, filter by all / active / done — and remembers the list in the browser between visits. It must be **usable the moment it is served**: no install step, no build step, no backend and no account. `` is the page title; `` is the single accent colour. ### Non-goals (do not build) - No backend, API, database or authentication. - No framework, package manager, bundler or third-party runtime — plain HTML, CSS and JavaScript. - No accounts, sharing, sync between devices or server persistence. - No service worker / offline caching, no analytics, no ads. - No admin panel, control panel, workflows or database layer. ## 2. The screen One route, `/` (the static `index.html`). It contains: - a **header** with ``; - a **composer** — a labelled text input plus an Add control (Enter also submits); - a **list** of todos, each row: a checkbox/toggle ("done"), the title, a Delete control (visible or on row hover/focus — always reachable by keyboard); - a **footer** with the count of remaining active todos, the filters (All / Active / Done) and a **Clear completed** action shown only when at least one todo is done; - an explicit **empty state** when there are no todos (and a distinct message when the filter has no matches), and a **hidden/disabled** Delete/Checkbox state that never leaves a row inaccessible. No routing beyond the single page; `/404` is not required. ## 3. Data model One entity, persisted as a JSON array in `localStorage`: - **Todo** — `id` (unique, e.g. a stringified timestamp or `crypto.randomUUID()`), `title` (non-empty, trimmed), `done` (boolean), `createdAt` (ISO timestamp). Storage contract: a single documented key holds the array (e.g. `simple-todo.items`). A missing, empty or unparseable value is treated as an empty list — never throw. Unknown fields are ignored; a malformed stored item is dropped, not rendered as a broken row. The filter selection is UI state and does **not** need to persist (persisting just the filter is optional). ## 4. Key flows 1. **Add.** The user types a non-empty title and presses Add (or Enter): the todo appears at the top of the list, the input clears and refocuses. Whitespace-only input is rejected (the Add control is disabled and the input stays unchanged). 2. **Toggle.** Clicking the checkbox / row toggles `done`; the title gets a struck-through done style and the remaining count updates. 3. **Delete.** The Delete control removes the row; the list and the empty state update immediately. 4. **Clear completed.** Shown only when at least one todo is done; removes every done todo after the built-in confirmation (a native `confirm()` is acceptable) and updates the list. 5. **Filter.** All / Active / Done switches the visible rows; the active filter is visibly marked and exposed to assistive tech (`aria-pressed` or equivalent). The footer count reflects the active todos regardless of the filter. 6. **Persist.** Every mutation writes the list to `localStorage`; reloading the page restores the list exactly (order, done state, titles) and the correct remaining count. ## 5. Functional requirements - **Input validation:** titles are trimmed; empty/whitespace titles are never added; long titles wrap instead of overflowing; a sensible maximum is enforced (e.g. 200 characters, hinted to the user) rather than silently truncating stored data. - **Rendering:** the list is rendered from the stored array (a single `render()` function is enough); the DOM never accumulates stale rows across re-renders. - **Counts:** the "X active" count updates on every add / toggle / delete / clear / filter change. - **Keyboard:** everything the mouse can do is reachable and operable with the keyboard; focus is visible and focus order follows the visual order. The composer supports Enter to add. - **Safe text:** todo titles are inserted as **text**, never as HTML (no `innerHTML` with user input), so a title like `` renders as literal text. - **No network:** the page makes no requests; it works when opened directly from the filesystem. ## 6. Non-functional requirements - **Dependency-free:** plain HTML + CSS + JavaScript. ES modules or one `