feat: responsive + a11y sweep across the remaining web routes #558

Closed
opened 2026-04-29 22:55:31 +00:00 by claude-desktop · 1 comment
Collaborator

As an operator, I want every web route to match the responsive + a11y baseline that the agents page just established so that the dashboard is usable on mobile and clears WCAG 2.1 AA across the board.

The agents page (/agents) is the reference. PR #555 nailed: lucide-react icons (no Unicode glyphs), <Button> for every clickable, mobile breakpoints (sm:), <Drawer> primitive with focus trap + focus return + role=dialog, autosave + role="status" live region, form inputs linked to errors via aria-describedby + aria-invalid, <label> wrapping, prefers-reduced-motion honored, contrast bumped on text-text-dim. Other routes are not yet there.

This is a sweep, route by route. Each ticket box below is a self-contained PR-sized chunk.

Acceptance criteria

Foundation — pre-work (do these first, blocks the others)

  • Audit apps/web/src/components/ for any drawer / panel still using <aside> directly. Migrate to <Drawer>.
  • If a <Tabs> primitive does not exist, add one with arrow-key navigation + role="tablist" / role="tab" / aria-selected. Likely needed by the planner / specs unification (#557) and by Settings.
  • Document the conventions in apps/web/CLAUDE.md: every clickable goes through <Button>, every overlay through <Drawer>, every icon from lucide.

/flows (apps/web/src/routes/flows.index.tsx — 385 LOC + features ~11.5K LOC across 37 files)

  • Replace raw <button> instances with <Button> (size + variant per existing usage).
  • Replace any Unicode glyphs (▶ ▼ ⚠ ✓ ✗ ⏸ …) with lucide icons.
  • Three filter <select> (lines ~303, 332, 346) + search <input> (line ~320) get aria-label + aria-describedby if errors exist.
  • Flows table responsive: stack as cards on mobile (mirror the InstancesTable pattern in agents.tsx).
  • RunsDrawer migrates to the <Drawer> primitive (drops the manual scaffold).
  • Mobile breakpoints (sm:) applied to header, filters, table.

/monitor (apps/web/src/routes/monitor.index.tsx — 503 LOC + components)

  • Audit task drawer / pipeline detail drawer — migrate to <Drawer> if they use the manual aside pattern.
  • Audit Recharts widgets for missing aria-label on chart wrappers.
  • Action buttons (cancel task, retry, etc.) → <Button> with tone="error" for destructive ones.
  • Mobile: pipeline list uses card stack below sm:.

/planner and /specs — covered by #557 (unification ticket). This sweep does not double-do that work.

/settings (apps/web/src/routes/settings.index.tsx — 522 LOC)

  • glyph (line ~325) → lucide Check.
  • All <input> / <select> get associated <label> (or aria-label).
  • Repo config rows: card layout on mobile.
  • Save status (if any inline) wrapped in role="status" aria-live="polite".

/login (apps/web/src/routes/login.tsx)

  • Form inputs have <label> + aria-describedby for the inline error.
  • Mobile: form fits 320px viewport.
  • Submit button uses loading={mutation.isPending} from the <Button> primitive.

Cross-route

  • Top nav (app-shell.tsx) collapses to a hamburger drawer below sm:.
  • Toast container (components/toasts.tsx) — toasts dismissable via Esc + tab order respects focus.
  • All <aside> drawer instances repo-wide migrated to <Drawer>. Grep apps/web/src for <aside and verify each.
  • All raw <button type="button"> outside of <Drawer>'s internals migrated to <Button>.
  • Run a final git grep -E "[▶▼⏸✓✗⚠①②③↺×↑↓⛽]" across apps/web/src and confirm only intentional non-icon Unicode (e.g. tier numerals in semantic contexts) remains.

Verification

  • Manual viewport check at 360px, 768px, 1024px, 1440px on every route.
  • Keyboard-only walk-through: every action reachable via Tab; Esc closes overlays; focus visible; focus returns on close.
  • Lighthouse a11y ≥ 95 on each route.
  • just qa passes; no Biome lint/a11y/* errors.

Out of scope

  • The /stats and /usage routes — being deleted in #556.
  • The /agents route — already done.
  • Token palette redesign — text-text-dim already bumped; the rest of the palette stays.
  • Adding new features to any route — strictly a sweep.
  • E2E test rewrites beyond what changes for accessibility (e.g. updated testids).

Suggested implementation order

  1. Foundation (Tabs primitive + CLAUDE.md doc) — unblocks the rest.
  2. /flows — biggest surface; locks the table / drawer migration patterns.
  3. /monitor — drawers + charts.
  4. /settings and /login — smaller, can land in parallel.
  5. Cross-route polish — top nav drawer + toast a11y + final glyph grep.

References

  • Agents-page model: PR #555. Key commits — 26e72f6 (lucide + Button extension), da25428 (Drawer primitive), 9fab22d (header disclosure + tier sr-only + reduced-motion + save live region), b72eebb (form labels), 9b61e4a (text-dim contrast bump).
  • Existing primitives: apps/web/src/components/{button,drawer,chip-input}.tsx.
  • Hook: apps/web/src/lib/use-media-query.ts for runtime branching by viewport.
  • Convention doc: apps/web/CLAUDE.md (radius / shadow / drawer patterns).
As an operator, I want every web route to match the responsive + a11y baseline that the agents page just established so that the dashboard is usable on mobile and clears WCAG 2.1 AA across the board. The agents page (`/agents`) is the reference. PR #555 nailed: lucide-react icons (no Unicode glyphs), `<Button>` for every clickable, mobile breakpoints (`sm:`), `<Drawer>` primitive with focus trap + focus return + role=dialog, autosave + `role="status"` live region, form inputs linked to errors via `aria-describedby` + `aria-invalid`, `<label>` wrapping, `prefers-reduced-motion` honored, contrast bumped on `text-text-dim`. Other routes are not yet there. This is a sweep, route by route. Each ticket box below is a self-contained PR-sized chunk. ## Acceptance criteria ### Foundation — pre-work (do these first, blocks the others) - [ ] Audit `apps/web/src/components/` for any drawer / panel still using `<aside>` directly. Migrate to `<Drawer>`. - [ ] If a `<Tabs>` primitive does not exist, add one with arrow-key navigation + `role="tablist"` / `role="tab"` / `aria-selected`. Likely needed by the planner / specs unification (#557) and by Settings. - [ ] Document the conventions in `apps/web/CLAUDE.md`: every clickable goes through `<Button>`, every overlay through `<Drawer>`, every icon from lucide. ### `/flows` (`apps/web/src/routes/flows.index.tsx` — 385 LOC + features ~11.5K LOC across 37 files) - [ ] Replace raw `<button>` instances with `<Button>` (size + variant per existing usage). - [ ] Replace any Unicode glyphs (`▶ ▼ ⚠ ✓ ✗ ⏸` …) with lucide icons. - [ ] Three filter `<select>` (lines ~303, 332, 346) + search `<input>` (line ~320) get `aria-label` + `aria-describedby` if errors exist. - [ ] Flows table responsive: stack as cards on mobile (mirror the `InstancesTable` pattern in `agents.tsx`). - [ ] `RunsDrawer` migrates to the `<Drawer>` primitive (drops the manual scaffold). - [ ] Mobile breakpoints (`sm:`) applied to header, filters, table. ### `/monitor` (`apps/web/src/routes/monitor.index.tsx` — 503 LOC + components) - [ ] Audit task drawer / pipeline detail drawer — migrate to `<Drawer>` if they use the manual aside pattern. - [ ] Audit Recharts widgets for missing `aria-label` on chart wrappers. - [ ] Action buttons (cancel task, retry, etc.) → `<Button>` with `tone="error"` for destructive ones. - [ ] Mobile: pipeline list uses card stack below `sm:`. ### `/planner` and `/specs` — covered by #557 (unification ticket). This sweep does not double-do that work. ### `/settings` (`apps/web/src/routes/settings.index.tsx` — 522 LOC) - [ ] `✓` glyph (line ~325) → lucide `Check`. - [ ] All `<input>` / `<select>` get associated `<label>` (or `aria-label`). - [ ] Repo config rows: card layout on mobile. - [ ] Save status (if any inline) wrapped in `role="status" aria-live="polite"`. ### `/login` (`apps/web/src/routes/login.tsx`) - [ ] Form inputs have `<label>` + `aria-describedby` for the inline error. - [ ] Mobile: form fits 320px viewport. - [ ] Submit button uses `loading={mutation.isPending}` from the `<Button>` primitive. ### Cross-route - [ ] Top nav (`app-shell.tsx`) collapses to a hamburger drawer below `sm:`. - [ ] Toast container (`components/toasts.tsx`) — toasts dismissable via Esc + tab order respects focus. - [ ] All `<aside>` drawer instances repo-wide migrated to `<Drawer>`. Grep `apps/web/src` for `<aside` and verify each. - [ ] All raw `<button type="button">` outside of `<Drawer>`'s internals migrated to `<Button>`. - [ ] Run a final `git grep -E "[▶▼⏸✓✗⚠①②③↺×↑↓⛽]"` across `apps/web/src` and confirm only intentional non-icon Unicode (e.g. tier numerals in semantic contexts) remains. ### Verification - [ ] Manual viewport check at 360px, 768px, 1024px, 1440px on every route. - [ ] Keyboard-only walk-through: every action reachable via Tab; Esc closes overlays; focus visible; focus returns on close. - [ ] Lighthouse a11y ≥ 95 on each route. - [ ] `just qa` passes; no Biome `lint/a11y/*` errors. ## Out of scope - The `/stats` and `/usage` routes — being deleted in #556. - The `/agents` route — already done. - Token palette redesign — `text-text-dim` already bumped; the rest of the palette stays. - Adding new features to any route — strictly a sweep. - E2E test rewrites beyond what changes for accessibility (e.g. updated testids). ## Suggested implementation order 1. Foundation (Tabs primitive + CLAUDE.md doc) — unblocks the rest. 2. `/flows` — biggest surface; locks the table / drawer migration patterns. 3. `/monitor` — drawers + charts. 4. `/settings` and `/login` — smaller, can land in parallel. 5. Cross-route polish — top nav drawer + toast a11y + final glyph grep. ## References - Agents-page model: PR #555. Key commits — `26e72f6` (lucide + Button extension), `da25428` (Drawer primitive), `9fab22d` (header disclosure + tier sr-only + reduced-motion + save live region), `b72eebb` (form labels), `9b61e4a` (text-dim contrast bump). - Existing primitives: `apps/web/src/components/{button,drawer,chip-input}.tsx`. - Hook: `apps/web/src/lib/use-media-query.ts` for runtime branching by viewport. - Convention doc: `apps/web/CLAUDE.md` (radius / shadow / drawer patterns).
Author
Collaborator

Dispatch coordination

Parallel with #556. Soft-blocks #557 — see ordering note below.

Routing: boss (Tabs primitive needs design judgment; without it, #557 will reinvent it).

Hard ordering constraint — Foundation commit MUST land first:
The "Foundation — pre-work" block at the top of the acceptance criteria is the gating piece for #557. Land it as the first commit on this branch and push immediately so #557 can rebase off it. Specifically:

  1. Add the <Tabs> primitive at apps/web/src/components/tabs.tsx with arrow-key navigation + role="tablist" + aria-selected.
  2. Audit apps/web/src/components/ for any leftover <aside> drawers and migrate to <Drawer>.
  3. Update apps/web/CLAUDE.md with the conventions (Button / Drawer / Tabs / lucide / tokens — only).

Commit, push, then proceed with the per-route sweep (/flows/monitor/settings/login → cross-route). Do not gate the sweep on #557; #557 will rebase itself.

Conflict points to watch:

  • apps/web/src/components/app-shell.tsx#556 drops the /stats nav entry; #557 collapses Specs+Planner. Coordinate via rebase.
  • apps/web/CLAUDE.md#557 will also append a section. Land your update first; #557 rebases.
  • Tabs primitive — owned by this ticket. #557 imports it.

Independent of: #557 specs/planner unification (different feature folders, but consumes our Tabs).

## Dispatch coordination **Parallel with #556.** Soft-blocks #557 — see ordering note below. **Routing:** boss (Tabs primitive needs design judgment; without it, #557 will reinvent it). **Hard ordering constraint — Foundation commit MUST land first:** The "Foundation — pre-work" block at the top of the acceptance criteria is the gating piece for #557. Land it as the **first commit** on this branch and push immediately so #557 can rebase off it. Specifically: 1. Add the `<Tabs>` primitive at `apps/web/src/components/tabs.tsx` with arrow-key navigation + `role="tablist"` + `aria-selected`. 2. Audit `apps/web/src/components/` for any leftover `<aside>` drawers and migrate to `<Drawer>`. 3. Update `apps/web/CLAUDE.md` with the conventions (Button / Drawer / Tabs / lucide / tokens — only). Commit, push, then proceed with the per-route sweep (`/flows` → `/monitor` → `/settings` → `/login` → cross-route). Do not gate the sweep on #557; #557 will rebase itself. **Conflict points to watch:** - `apps/web/src/components/app-shell.tsx` — #556 drops the `/stats` nav entry; #557 collapses Specs+Planner. Coordinate via rebase. - `apps/web/CLAUDE.md` — #557 will also append a section. Land your update first; #557 rebases. - Tabs primitive — owned by this ticket. #557 imports it. **Independent of:** #557 specs/planner unification (different feature folders, but consumes our Tabs).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
charles/claude-hooks#558
No description provided.