Files
Panama/docs/superpowers/specs/2026-08-24-accessibility-redesign.md

4.9 KiB
Raw Permalink Blame History

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.05.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.