Problem
Modals in the app have inconsistent accessibility. Only SettingsModal and DetailPanel are fully correct.
| Modal |
role=dialog |
aria-modal |
focus management |
SettingsModal.svelte:86 |
✅ |
✅ + aria-label |
❌ no focus move/trap |
DetailPanel.svelte |
✅ |
✅ |
❌ |
LifecycleView.svelte:295 (Run Cycle) |
✅ |
❌ |
❌ |
ConsolidatePanel.svelte (Confirm Remove) |
✅ |
❌ |
❌ |
MemoryEditor.svelte:169 (modal-overlay) |
❌ |
❌ |
❌ |
ui/Modal.svelte (shared primitive) |
❌ |
❌ |
❌ (Escape ✅) |
Key gaps: keyboard Tab can escape behind the overlay on all modals; destructive-action modals (Run Cycle, Remove duplicates) are the least accessible.
There is a shared ui/Modal.svelte (Escape handling, backdrop, scale transition) but most modals don't use it — every component rolls its own.
Fix
- Harden
ui/Modal.svelte as the single modal primitive: add role="dialog", aria-modal="true", aria-label, focus-on-open (focus first focusable / Cancel button), basic focus trap (Tab/Shift+Tab cycle), restore focus on close
- Migrate modals to it — priority order: ConsolidatePanel (destructive) → LifecycleView (destructive) → MemoryEditor → keep SettingsModal/DetailPanel markup but add focus handling
Source
Full-app UI audit (a11y; destructive flows get priority).
Problem
Modals in the app have inconsistent accessibility. Only SettingsModal and DetailPanel are fully correct.
SettingsModal.svelte:86DetailPanel.svelteLifecycleView.svelte:295(Run Cycle)ConsolidatePanel.svelte(Confirm Remove)MemoryEditor.svelte:169(modal-overlay)ui/Modal.svelte(shared primitive)Key gaps: keyboard Tab can escape behind the overlay on all modals; destructive-action modals (Run Cycle, Remove duplicates) are the least accessible.
There is a shared
ui/Modal.svelte(Escape handling, backdrop, scale transition) but most modals don't use it — every component rolls its own.Fix
ui/Modal.svelteas the single modal primitive: addrole="dialog",aria-modal="true",aria-label, focus-on-open (focus first focusable / Cancel button), basic focus trap (Tab/Shift+Tab cycle), restore focus on closeSource
Full-app UI audit (a11y; destructive flows get priority).