Accessible task board
Create a small task board without a framework. Users can add tasks, mark them complete, filter the list and reload the page without losing their work. The project emphasises state design and accessible DOM updates rather than visual effects.
Skills you will practise
- arrays and objects
- DOM events
- form validation
- localStorage
- accessible rendering
Project requirements
- A task has id, title and completed fields.
- Reject blank titles and announce the error beside the form.
- Support All, Active and Completed filters.
- Persist only the task data and selected filter.
- Use buttons with readable labels and announce changes through an aria-live status region.
Build it in stages
- 1
Define state and rendering
Keep tasks and the active filter in one state object. Write a render function that derives the visible list without mutating the stored tasks.
- 2
Add validated tasks
Handle form submit, trim the title, display an inline error for empty input and append a new task with a stable id.
- 3
Toggle and filter
Use event delegation on the list. Replace the changed task object rather than scattering DOM-only state across checkboxes.
- 4
Persist safely
Serialise the minimal state after every valid change. On startup, parse storage inside a try/catch and fall back to an empty board when data is invalid.
- 5
Check accessibility
Test keyboard-only use, visible focus, unique labels, empty states and announcements after add, toggle and filter actions.
Starter code
const state = { tasks: [], filter: 'all' };
function visibleTasks() {
// Return the tasks allowed by state.filter.
}
function render() {
// Rebuild the list and update the empty state.
}
Open the JavaScript playground
Expected result
A keyboard-usable task board that survives reloads, never accepts a blank task, filters without losing data and clearly announces each state change.
Progressive hints
Hint 1
Filter into a derived array; do not delete hidden tasks.
Hint 2
Use crypto.randomUUID() when available and a timestamp fallback otherwise.
Hint 3
Set textContent rather than innerHTML for user-entered titles.
Solution guidance
Show the approach after you attempt the project
Treat the task array as the source of truth and the DOM as a view of it. Every event creates the next state, saves it and calls render. This prevents filter changes from destroying tasks and keeps persistence predictable. User titles must be assigned through textContent, which avoids HTML injection. Inline validation connects the message to the input, while a separate polite live region announces successful actions without stealing focus.