Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ jobs:
fi

- name: Run Gourmand
run: gourmand --full .
run: gourmand .

javascript-tests:
name: JavaScript Tests
Expand Down
97 changes: 97 additions & 0 deletions .specify/specs/001-accessibility-audit/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Implementation Plan: Accessibility Audit & Fixes

**Branch**: `feature/73-accessibility-audit` | **Date**: 2026-05-10 | **Spec**: [spec.md](spec.md)
**Input**: Feature specification from `/specs/001-accessibility-audit/spec.md`

## Summary

Run a comprehensive WCAG 2.1 AA accessibility audit on Acquacotta, then fix all critical and serious issues. The app is a single-page Flask app with a dark theme, tab-based navigation, timer controls, manual entry modal, charts, and settings. All UI is in `templates/index.html` with vanilla JS — no build step, no framework.

## Technical Context

**Language/Version**: Python 3.x (Flask backend), Vanilla JS/HTML/CSS (frontend)
**Primary Dependencies**: Flask, Chart.js, Flatpickr
**Storage**: IndexedDB (browser), Google Sheets API
**Testing**: Lighthouse CLI, axe-core (browser), manual keyboard testing
**Target Platform**: Desktop + mobile browsers
**Constraints**: No JS frameworks, no build step, single HTML template

## Constitution Check

| Principle | Impact | Status |
|-----------|--------|--------|
| Privacy by Design | No analytics added | PASS |
| User Data Ownership | No data changes | PASS |
| Simplicity & Focus | Accessibility is usability, not feature creep | PASS |
| Timer Agnosticism | Manual entry gets equal a11y treatment | PASS |
| Offline-First | No network changes | PASS |
| Container-Ready | No deployment changes | PASS |

## Implementation Approach

### Phase 1: Automated Audit (Baseline)

1. Build and run the container locally
2. Run Lighthouse accessibility audit via Chrome DevTools or CLI
3. Run axe-core via browser extension or bookmarklet
4. Document all findings with severity levels

### Phase 2: Semantic HTML & ARIA (FR-001 through FR-006)

Files to modify: `templates/index.html`

- Add `<main>` landmark around content
- Add skip navigation link
- Convert nav buttons to proper `role="tablist"` / `role="tab"` / `role="tabpanel"` pattern
- Add `aria-label` to all icon-only buttons (PiP, navigation arrows)
- Add `<label>` elements or `aria-label` to all form inputs
- Add `aria-live="polite"` region for timer status and sync announcements
- Add focus trap to manual entry modal and any confirmation dialogs
- Add `aria-hidden="true"` to decorative elements

### Phase 3: Visual Accessibility (FR-007 through FR-010)

Files to modify: `templates/index.html` (CSS section)

- Fix color contrast: adjust `--text-secondary` and any other failing colors
- Add visible focus indicators (`:focus-visible` styles)
- Ensure touch targets are 44x44px minimum on mobile
- Test all changes against AA contrast requirements

### Phase 4: Chart Accessibility (FR-011)

Files to modify: `templates/index.html` (JS section)

- Add `role="img"` and `aria-label` to chart canvases with summary descriptions
- Consider adding a visually-hidden data summary for screen readers

### Phase 5: Verification

- Re-run Lighthouse — target score >= 90
- Re-run axe-core — zero critical/serious violations
- Manual keyboard walkthrough of all views
- Screen reader spot-check (VoiceOver or NVDA)

## Project Structure

### Documentation (this feature)

```text
specs/001-accessibility-audit/
├── spec.md # Feature specification
└── plan.md # This file
```

### Source Code Changes

```text
templates/
└── index.html # All HTML, CSS, and JS changes (semantic markup, ARIA, contrast, focus)
templates/
├── privacy.html # Minor: add skip link, landmark roles if missing
└── terms.html # Minor: add skip link, landmark roles if missing
```

## Complexity Tracking

No constitution violations. This is a pure enhancement to existing UI with no new dependencies, no data changes, and no architecture impact.
102 changes: 102 additions & 0 deletions .specify/specs/001-accessibility-audit/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Feature Specification: Accessibility Audit & Fixes

**Feature Branch**: `feature/73-accessibility-audit`
**Created**: 2026-05-10
**Status**: Draft
**Input**: GitHub Issue #73 — Run accessibility audit and fix identified issues

## User Scenarios & Testing *(mandatory)*

### User Story 1 - Keyboard-Only Timer Operation (Priority: P1)

A user who cannot use a mouse navigates the entire timer workflow using only the keyboard: selecting a preset duration, entering a task name, choosing a type, starting/pausing/stopping the timer, and logging a completed pomodoro.

**Why this priority**: Timer operation is the core function. If keyboard users can't operate it, the app is fundamentally inaccessible.

**Independent Test**: Tab through the timer view from nav to log completion. Every interactive element must be reachable and operable with Enter/Space.

**Acceptance Scenarios**:

1. **Given** the timer view is active, **When** a user presses Tab repeatedly, **Then** focus moves through all preset buttons, name input, type select, and action buttons in logical order
2. **Given** a timer preset button has focus, **When** the user presses Enter or Space, **Then** the preset is selected and visually indicated
3. **Given** the timer is running, **When** the user tabs to the Stop button and presses Enter, **Then** the timer stops and the log prompt appears

---

### User Story 2 - Screen Reader Navigation (Priority: P1)

A screen reader user can understand the page structure, identify all controls, and receive feedback when actions occur (timer starts, pomodoro logged, sync status changes).

**Why this priority**: Without proper ARIA labels and semantic HTML, screen reader users cannot use the app at all.

**Independent Test**: Navigate with a screen reader (NVDA/VoiceOver). All controls announce their purpose; status changes are announced via live regions.

**Acceptance Scenarios**:

1. **Given** a screen reader is active, **When** the user navigates the page, **Then** all buttons, inputs, and sections have descriptive accessible names
2. **Given** the timer starts, **When** the countdown updates, **Then** the timer state change is announced (start/pause/complete) without spamming every second
3. **Given** a pomodoro is logged, **When** the action completes, **Then** a live region announces success or failure

---

### User Story 3 - Sufficient Color Contrast (Priority: P2)

All text and interactive elements meet WCAG 2.1 AA contrast ratios (4.5:1 for normal text, 3:1 for large text and UI components).

**Why this priority**: The dark theme with muted secondary text colors likely has contrast issues that affect readability for low-vision users.

**Independent Test**: Run axe-core or Lighthouse on every view. All contrast violations are resolved.

**Acceptance Scenarios**:

1. **Given** the app uses `--text-secondary: #a0a0a0` on `--bg-secondary: #16213e`, **When** contrast is checked, **Then** the ratio meets 4.5:1 (currently ~3.8:1 — needs fix)
2. **Given** any UI element, **When** its contrast ratio is measured, **Then** it meets AA minimums

---

### User Story 4 - Manual Entry Form Accessibility (Priority: P2)

The manual entry modal is fully accessible: all fields have associated labels, the modal traps focus, and Escape closes it.

**Why this priority**: Manual entry is a first-class feature (Timer Agnosticism principle). It must be equally accessible.

**Independent Test**: Open the manual entry modal with keyboard, fill all fields, submit, and close — all without a mouse.

**Acceptance Scenarios**:

1. **Given** the History view is active, **When** the user activates "+ Add Manual", **Then** the modal opens and focus moves to the first field
2. **Given** the modal is open, **When** the user presses Tab at the last field, **Then** focus wraps to the first field (focus trap)
3. **Given** the modal is open, **When** the user presses Escape, **Then** the modal closes and focus returns to the trigger button

---

### Edge Cases

- What happens when the timer completes while focus is elsewhere on the page? (Live region should announce it)
- How does the slidable timer dial work with keyboard? (Must support arrow keys)
- Are chart.js visualizations in Reports accessible? (Need text alternatives or data tables)

## Requirements *(mandatory)*

### Functional Requirements

- **FR-001**: All interactive elements MUST be keyboard accessible (Tab, Enter, Space, Escape, Arrow keys)
- **FR-002**: All form inputs MUST have associated `<label>` elements or `aria-label` attributes
- **FR-003**: All buttons MUST have accessible names (visible text or `aria-label`)
- **FR-004**: Navigation MUST use `role="tablist"` / `role="tab"` pattern or equivalent semantic markup
- **FR-005**: Modals MUST implement focus trap and restore focus on close
- **FR-006**: Status changes (timer start/stop/complete, sync status) MUST use `aria-live` regions
- **FR-007**: Color contrast MUST meet WCAG 2.1 AA (4.5:1 normal text, 3:1 large text/UI components)
- **FR-008**: Focus indicators MUST be visible on all interactive elements
- **FR-009**: Page MUST include a skip navigation link
- **FR-010**: Touch targets MUST be at least 44x44 CSS pixels on mobile
- **FR-011**: Charts MUST have text alternatives (summary or data table)

## Success Criteria *(mandatory)*

### Measurable Outcomes

- **SC-001**: Lighthouse accessibility score >= 90 on all pages (index, privacy, terms)
- **SC-002**: Zero critical or serious axe-core violations
- **SC-003**: All timer and manual entry controls are operable with keyboard only
- **SC-004**: All text meets WCAG 2.1 AA contrast ratios
Loading
Loading