templates.SimpleTodo/SPEC.md

124 lines
7.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 (`<Project name>`, `<accent>`) 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. `<Project name>` is the page title; `<accent>` 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 `<Project name>`;
- 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 `<img src=x onerror=alert(1)>` 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 `<script>` are both fine;
a `package.json` / bundler is not. The page must run from `file://` and from a static server.
- **Performance:** a single small page; first render well under 1 s on a normal machine; no
render-blocking third-party assets.
- **Responsive:** usable from 360px wide up to desktop; no horizontal scroll at 390×844.
- **Accessibility (AA):** semantic landmarks (`header`/`main`/`footer`), a real `<label>` for the
composer input, the checkbox as an accessible control (or a button with `aria-pressed`),
≥ AA contrast for the `<accent>` colour and the done style, visible focus.
- **Resilience:** corrupt/empty storage degrades to an empty list; no unhandled errors in the
console during normal use.
- **Security:** no secrets; user input is rendered as text only.
## 7. Acceptance criteria (definition of done)
- [ ] The project is a static site: no `package.json`, no bundler, no third-party runtime, no network calls.
- [ ] Served at `/`, the page loads with no console errors and shows the empty state on a fresh browser.
- [ ] Adding, toggling, deleting and clearing todos all work; the remaining count is always correct.
- [ ] The All / Active / Done filter works, marks the active filter and shows a distinct no-match state.
- [ ] Reloading restores the list exactly from `localStorage`; a corrupted stored value degrades to an empty list instead of throwing.
- [ ] A title containing HTML is rendered as literal text (no injection).
- [ ] Whitespace-only titles cannot be added; long titles wrap and do not overflow.
- [ ] Everything is operable by keyboard alone, with visible focus and AA contrast.
- [ ] Usable at 390×844 with no horizontal scroll.
- [ ] The README quickstart (how to serve it, how to use it) works on a clean clone.
- [ ] The dev server (template `devServer` section) serves the page at its port.
## 8. Suggested build order
This is a small project — keep it top-down and verify each step before the next:
1. **Skeleton** — `index.html` with the header, composer, list and footer, styled with the accent
colour; empty state visible. Serve it and confirm it loads.
2. **Model + storage** — the `Todo` shape, load/save to `localStorage` (with the corrupt-value
fallback), and rendering the list from the stored array.
3. **Interactions** — add, toggle, delete, clear-completed, filters, counts, keyboard support.
4. **Polish + quality** — responsive layout, accessibility pass, injection-safe rendering, README
quickstart, and walk every acceptance box.