Files
whisper_voice/docs/plans/2026-02-18-wcag-aaa-design.md
Your Name 937061f710 Docs: Add WCAG 2.2 AAA compliance design document
Comprehensive design covering color system overhaul, component fixes,
accessibility properties, keyboard navigation, and reduced motion support.
2026-02-18 20:53:25 +02:00

7.3 KiB

WCAG 2.2 AAA Compliance Design

Date: 2026-02-18 Approach: Style-System-First (cascade from SettingsStyle.qml, then per-component fixes) Goal: Full WCAG 2.2 AAA compliance while preserving the current visual identity


Section 1: Color System Overhaul (SettingsStyle.qml)

Update the central design token singleton to meet AAA contrast ratios.

Token Current New Rationale
accentPurple #7000FF (1.6:1) #B794F6 (7.2:1) AAA normal text on #121212
textSecondary #999999 (6.3:1) #ABABAB (8.1:1) AAA normal text
borderSubtle rgba(1,1,1,0.08) rgba(1,1,1,0.22) 3:1 non-text contrast

New tokens to add:

  • textDisabled: "#808080" (4.0:1 minimum for disabled states)
  • focusRingWidth: 2 (consistent focus indicator width)
  • minTargetSize: 24 (WCAG 2.5.8 minimum target)

Section 2: Component Default Fixes

Bulk fixes that cascade from component-level defaults.

ModernSlider.qml:

  • Handle: 18x18 -> 24x24 (meets 24px target size)
  • Track: 4px -> 6px height
  • Add 2px focus ring on activeFocus
  • Add Accessible.role: Accessible.Slider

ModernSwitch.qml:

  • Add "I" (on) / "O" (off) pip marks inside the thumb for non-color state indication
  • Switch border to borderSubtle token
  • Add Accessible.role: Accessible.CheckBox with state in name

ModernTextField.qml:

  • Placeholder: #606060 -> #808080 (3.7:1 minimum)
  • Focus border: 1px -> 2px
  • Add Accessible.role: Accessible.EditableText

ModernComboBox.qml:

  • Border -> borderSubtle token
  • Focus border: 1px -> 2px
  • Arrow icon: #888888 -> #ABABAB
  • Add Accessible.role: Accessible.ComboBox

ModernKeySequenceRecorder.qml:

  • Border -> borderSubtle token
  • Focus border: 1px -> 2px
  • "None" text: #808080 -> #ABABAB
  • Add activeFocusOnTab: true

GlowButton.qml:

  • Default text: #9499b0 -> #ABABAB
  • Border -> borderSubtle token

Section 3: Individual Component Attention

Per-file hardcoded color fixes that can't be solved by token changes alone.

Settings.qml:

  • StatBox accent colors: #bd93f9 -> #CAA9FF, #ff79c6 -> #FF8FD0, #ff5555 -> #FF8A8A
  • Close button hover: #ff4b4b -> #FF8A8A
  • Model info text: 10px -> 11px, remove opacity: 0.6
  • INSTALLED tag: verify contrast of green-on-dark

Loader.qml:

  • Subtitle: #80ffffff -> #ABABAB
  • Status text: remove opacity: 0.8, use full textSecondary
  • Border: #40ffffff -> rgba(1,1,1,0.22)

Overlay.qml:

  • Border: #40ffffff -> rgba(1,1,1,0.22) (non-text contrast)

Section 4: Accessibility Properties

Add Accessible.name and Accessible.role to every interactive and informational element.

Window-level:

  • Overlay.qml: Accessible.name: "WhisperVoice Overlay", title: "WhisperVoice"
  • Settings.qml: Accessible.name: "WhisperVoice Settings", title: "WhisperVoice Settings"
  • Loader.qml: Accessible.name: "WhisperVoice Loading", title: "WhisperVoice"

Overlay.qml elements:

  • Mic button: Accessible.name: ui.isRecording ? "Stop recording" : "Start recording", Accessible.role: Accessible.Button
  • Status text: Accessible.name: statusText.text, Accessible.role: Accessible.AlertMessage
  • Transcription text: Accessible.name: transcriptionText.text, Accessible.role: Accessible.StaticText

Settings.qml elements:

  • Nav items: Accessible.name: modelData, Accessible.role: Accessible.Tab
  • Close button: Accessible.name: "Close settings", Accessible.role: Accessible.Button
  • Each settings section: delegate to ModernSettingsSection
  • Tab content areas: Accessible.role: Accessible.PageTab

Shared components:

  • ModernSettingsSection: Accessible.name: root.title + " settings group", Accessible.role: Accessible.Grouping
  • ModernSettingsItem: Accessible.name: root.label, Accessible.role: Accessible.Row

Section 5: Keyboard Navigation

Make every interactive element operable via keyboard alone.

Overlay.qml mic button:

  • Add activeFocusOnTab: true
  • Add Keys.onReturnPressed / Keys.onSpacePressed -> toggle recording
  • Add visible 2px focus ring using activeFocus state

Settings.qml nav sidebar:

  • Add activeFocusOnTab: true to each nav item
  • Add Keys.onReturnPressed / Keys.onSpacePressed -> select tab
  • Add Keys.onUpPressed / Keys.onDownPressed -> move between tabs
  • Visible focus ring on each nav item

Settings.qml close button:

  • Add activeFocusOnTab: true
  • Add Keys.onReturnPressed / Keys.onSpacePressed -> close

All custom controls:

  • Ensure activeFocusOnTab: true on ModernSlider, ModernSwitch, ModernComboBox, ModernKeySequenceRecorder, GlowButton
  • 2px focus ring visible when activeFocus is true

Section 6: Reduced Motion Support

Three-layer system: OS detection, user toggle, animation conditionals.

Config layer (config.py):

  • Add "reduce_motion": false to DEFAULT_SETTINGS

OS detection (main.py):

  • On Windows startup, call SystemParametersInfo(SPI_GETCLIENTAREAANIMATION) via ctypes
  • If OS says reduce motion, set config reduce_motion: true (user can override)

Bridge layer (bridge.py):

  • Add reduceMotion Q_PROPERTY (bool, notify signal)
  • Reads from config, exposed to QML as ui.reduceMotion

QML conditionals:

Overlay.qml animations (6 total):

  • Shimmer: running: !ui.reduceMotion
  • Gradient blobs shader: visible: !ui.reduceMotion, static fallback color
  • Glow shader: visible: !ui.reduceMotion
  • CRT shader: visible: !ui.reduceMotion
  • Particle emitter: enabled: !ui.reduceMotion
  • Rainbow wave shader: visible: !ui.reduceMotion

Loader.qml animations (2 total):

  • Icon pulse: running: !ui.reduceMotion
  • Progress shimmer: running: !ui.reduceMotion

UI toggle:

  • Add "Reduce Motion" toggle in Settings.qml Visuals tab
  • Bound to config reduce_motion via bridge

Section 7: Remaining WCAG AAA Criteria

7a. Status Messages (4.1.3):

  • Overlay status label gets Accessible.role: Accessible.AlertMessage (covered in Section 4)

7b. Text Scaling (1.4.4, 1.4.10):

  • Already handled by existing ui_scale config property. No changes needed.

7c. Page Titles (2.4.2):

  • Window titles set in Section 4 window-level properties.

7d. Error Identification (3.3.1):

  • Not applicable. No user-submitted forms with validation.

Files Changed (Summary)

File Changes
src/ui/qml/SettingsStyle.qml Token updates + new tokens
src/ui/qml/Overlay.qml Colors, accessibility, keyboard, reduced motion
src/ui/qml/Settings.qml Colors, accessibility, keyboard, reduce motion toggle
src/ui/qml/Loader.qml Colors, accessibility, reduced motion
src/ui/qml/ModernSlider.qml Size, focus ring, accessibility
src/ui/qml/ModernSwitch.qml I/O marks, border, accessibility
src/ui/qml/ModernTextField.qml Placeholder color, focus, accessibility
src/ui/qml/ModernComboBox.qml Border, arrow color, focus, accessibility
src/ui/qml/ModernKeySequenceRecorder.qml Border, colors, focus, accessibility
src/ui/qml/GlowButton.qml Text color, border, accessibility
src/ui/qml/ModernSettingsSection.qml Accessibility properties
src/ui/qml/ModernSettingsItem.qml Accessibility properties
src/core/config.py Add reduce_motion default
src/ui/bridge.py Add reduceMotion property
main.py Windows reduced-motion OS detection