chore(template): sync SPEC.md

This commit is contained in:
SemianiakaVY 2026-10-08 15:45:44 +00:00
parent cae955a4b6
commit bd1415e9e8

123
SPEC.md Normal file
View file

@ -0,0 +1,123 @@
# 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.