# Accessibility redesign — everything that's real Approved mock: `home-mocks/accessibility.html` (scratchpad, :8642). Spec wins over mock on conflict. Tabless leaf stays. ## Goals 1. **Magnifier keybinds**: Super+= / Super+- / Super+0 step the zoom from anywhere, through the shell so the stored preference stays truthful and the OSD shows the level. Mechanism: the binds call `qs ipc call accessibility zoom in|out|reset`; a new IPC target routes to `Accessibility.stepZoom()`, which computes (×1.25 steps, clamp to the schema's 1.0–5.0), commits through the normal verified-preference path, and posts the OSD. No new script; a dead shell means dead zoom keys, which is honest (everything else in the shell is dead too). 2. **Reduce motion becomes true**: `Theme.qml`'s `dur*` tokens gate on `Settings.animationsEnabled` (0 when off) so the bar/dock/OSD/panels genuinely still. 3. **Visual alerts**: new schema key `visualAlerts` (bool, def false, group `accessibility`); when on, a one-shot screen-edge flash (per-screen overlay, single animation per event, never looping) fires wherever `Notifs.playBell` decides a notification sounds (including when the audible bell is skipped for `sound: false` apps? No — the flash follows the same eligibility as the bell EXCEPT the eventSounds gate: visual alerts are for people who can't hear the bell, so the flash fires on bell-eligible notifications even when event sounds are off. Pin that.) 4. **Honest Orca control**: running state (pgrep poll on page open + after actions), Start/Stop; the a11y-bus readiness line. No gsettings screen-reader toggle (gsd-owned, inert — refused). 5. **The primitives get accessible**: `SliderRow`, `ToggleRow`, `SwitchRow`, `ActionRow`, `ChoiceRow`, `SegmentRow`, `SettingRow` (activatable), `OptionPickerRow`, `PickerRow` gain `Accessible.role`/`Accessible.name` (from label; detail as description), `activeFocusOnTab` on the interactive element, key handling (Space/Enter activates, Left/Right steps sliders and segments), and a visible focus indicator (the titlebar close-button border pattern). No API or visual-at-rest changes. 6. **Page rebuilt** per mock: Vision / Motion / Hearing / Keyboard & pointer / Screen reader; `dimStrength` gated on `dimInactive`; magnifier "1× is off" honesty (schema gains `unit: "×"`-style display via the page detail — fix the dead `zeroLabel`); the honesty box for sticky/slow/bounce with the probed-not-assumed copy; mono-audio NOT YET row; "Open Keyboard" jump. 7. Cleanups: `DESKTOP-PARITY.md`'s stale AccessX claim corrected; search entries for magnifier/zoom/orca/screen reader/high contrast/reduce motion/visual alerts. Non-goals: mono audio (deferred with the on-page honesty), sticky/slow/bounce/hover/mouse keys (refused with receipts), gsettings `enable-animations`/`screen-reader-enabled` writes (inert), shell text scaling. ## Ownership - **A**: `config/PreferenceSchema.qml` (visualAlerts; magnifierFactor detail/unit fix), `config/dot/hypr/keybinds.lua` (three zoom binds — CHECK for collisions with existing Super+=/-/0 binds first; pick free chords and update the mock copy via B if needed), `shell.qml` (accessibility IPC target), `services/Accessibility.qml` (stepZoom, orcaRunning + start/stop, poll discipline), `services/Notifs.qml` (flash trigger signal per goal 3), NEW `modules/notifications/VisualBell.qml` (+ per-screen wiring in shell.qml), `config/dot/hypr/DESKTOP-PARITY.md` (stale claim), `services/OsdState.qml`/`OsdModel.js` only if the zoom OSD needs a new kind (coordinate with C on osd-model-contract). - **B**: the nine row primitives (accessibility additions only — zero API/visual-at-rest change), `modules/settings/AccessibilityPage.qml` rebuild (+ any new components + qmldir). Reuse KeycapChord for the chord display. - **C**: `services/SettingsSearch.qml`, NEW `tests/quickshell/accessibility-contract` (page structure; zoom IPC path commits through the verified-preference path and never calls hyprctl keyword; visual bell one-shot — no `loops: Animation.Infinite`, no Timer-driven repeat; flash follows bell eligibility but not the eventSounds gate; primitives carry Accessible.name + activeFocusOnTab — sweep all nine files; Orca control uses process state, never gsettings), `osd-model-contract` (zoom kind if added), reconcile any contract pinning Theme durations or the primitives' file contents, backlog Phase 14, README count (174 → 175 expected), docs/commands regen at the end (schema changed). Hard rules: no live mutations (no zoom application, no orca start/stop, no gsettings writes, no hyprctl keyword/reload); hermetic stubs only; valid QML at every save (primitives are used by EVERY page — a broken SliderRow takes the whole settings app down, so B edits them one at a time with a reload-check between each). B programs against A's pinned APIs; A updates this spec before changing them.