feat: accessibility overhaul, agent-friendly API, standardized errors

P0 fixes:
- Image result cards are now real <a> links (open-in-new-tab, Cmd-click, copy-link)
- aria-label on all icon-only buttons (settings, clear, close, nav, toggles, delete)
- Search input semantics: label, name=q, type=search, combobox ARIA pattern
- Visible focus-visible ring on all interactive elements, removed bare outline-none

P1 fixes:
- Skip to main content link for keyboard navigation
- Proper heading hierarchy (h1/h2) and landmark elements (nav, main, aside)
- aria-live regions for loading/results/error announcements
- Custom favicon SVG (eliminates 404 console errors)
- Dialog/modal semantics with role=dialog and aria-modal

P2 API improvements:
- result_id, rank, engine on every result; timestamp, total_results, has_next on response
- Standardized APIError model (code/message/details/retry_hint) on all error responses
- X-Response-Time-Ms and rate-limit headers on all responses
- curl examples and sample JSON in OpenAPI docs for every endpoint
- Global exception handler returns consistent error JSON
- engines parameter in URL for reproducible sessions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-02-23 17:06:09 +11:00
co-authored by Copilot
parent bd22089d23
commit b36d3e53b5
16 changed files with 381 additions and 85 deletions
+35
View File
@@ -1,5 +1,40 @@
# Changelog
## 1.4.0
### Added
- **Accessibility overhaul (P0)**
- Image result cards are now real `<a>` links — supports open-in-new-tab, Cmd/Ctrl-click, and copy-link
- `aria-label` on all icon-only buttons (settings, clear, close, lightbox nav, engine toggles, domain remove, pagination arrows, error dismiss)
- Search input has proper semantics: `<label>`, `name="q"`, `type="search"`, `role="combobox"`, `aria-autocomplete`, `aria-expanded`, `aria-activedescendant`
- Autocomplete list uses `role="listbox"` and `role="option"` with `aria-selected`
- Visible keyboard focus (`focus-visible:ring-2`) on all interactive elements; removed bare `outline-none`
- **Page structure (P1)**
- "Skip to main content" link for keyboard users
- Proper heading hierarchy (`h1` on home, `h2` for results/no-results, `sr-only` screen-reader headings)
- `<nav>` landmarks for category tabs and pagination, `<aside>` for stats sidebar
- **Live announcements (P1)**
- `aria-live="polite"` region announces loading state and result counts to screen readers
- `aria-live="assertive"` on error toast for immediate announcement
- Loading spinner has `role="status"` with `sr-only` text
- **Custom favicon** — search magnifying glass SVG replaces default Vite favicon (eliminates 404 console errors)
- **Agent-friendly API response (P2)**
- Every result now includes `result_id` (stable unique ID), `rank` (position in result list), and `engine`
- `SearchResponse` includes `timestamp` (ISO 8601), `total_results`, and `has_next` for cursor-based pagination
- `EngineError` includes `code`, `details`, and `retry_hint` fields
- New `APIError` standard error model (`code`/`message`/`details`/`retry_hint`) used on all error responses
- Standardized 404 errors on engine and domain endpoints with consistent JSON schema
- **API documentation (P2)**
- All API endpoints include copy-paste `curl` examples and sample JSON responses in OpenAPI descriptions
- API version bumped to 1.4.0 with descriptive title
- **Rate-limit headers (P2)**
- `X-Response-Time-Ms` and `X-RateLimit-Limit`/`X-RateLimit-Remaining` headers on all responses
- Global exception handler returns standardized `APIError` JSON for unhandled errors
- **URL state (P2)**
- `engines` parameter supported in URL for reproducible sessions (`?q=hello&engines=google,bing`)
- Settings modal, lightbox, and category tabs all have `role="dialog"`, `aria-modal`, `aria-expanded`, `aria-pressed`, and `aria-current` where appropriate
## 1.3.0
### Added