15 tasks covering: design tokens, 6 component fixes, Settings/Overlay/Loader hardcoded colors, accessibility properties, keyboard nav, reduced motion.
1088 lines
30 KiB
Markdown
1088 lines
30 KiB
Markdown
# WCAG 2.2 AAA Implementation Plan
|
|
|
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
|
|
**Goal:** Bring WhisperVoice to full WCAG 2.2 AAA compliance while preserving the existing visual identity.
|
|
|
|
**Architecture:** Style-system-first approach. Update the central `SettingsStyle.qml` design token singleton first so color/contrast fixes cascade to all consumers. Then fix per-component issues (sizes, focus rings, I/O marks). Then add accessibility properties, keyboard navigation, and reduced-motion support through the Python bridge layer.
|
|
|
|
**Tech Stack:** PySide6/QML (Qt 6), Python 3.x, ctypes (Windows API for reduced motion detection)
|
|
|
|
---
|
|
|
|
### Task 1: Update SettingsStyle.qml Design Tokens
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/SettingsStyle.qml` (entire file, 25 lines)
|
|
|
|
**Step 1: Edit SettingsStyle.qml**
|
|
|
|
Replace the entire content with updated AAA-compliant tokens:
|
|
|
|
```qml
|
|
import QtQuick
|
|
|
|
pragma Singleton
|
|
|
|
QtObject {
|
|
// Colors
|
|
readonly property color background: "#F2121212" // Deep Obsidian with 95% opacity
|
|
readonly property color surfaceCard: "#1A1A1A" // Layer 1
|
|
readonly property color surfaceHover: "#2A2A2A" // Layer 2
|
|
readonly property color borderSubtle: Qt.rgba(1, 1, 1, 0.22) // WCAG 3:1 non-text contrast
|
|
|
|
readonly property color textPrimary: "#FAFAFA"
|
|
readonly property color textSecondary: "#ABABAB" // WCAG AAA 8.1:1 on #121212
|
|
readonly property color textDisabled: "#808080" // 4.0:1 minimum for disabled states
|
|
|
|
readonly property color accentPurple: "#B794F6" // WCAG AAA 7.2:1 on #121212
|
|
readonly property color accentCyan: "#00F2FF"
|
|
|
|
// Configurable active accent
|
|
property color accent: accentPurple
|
|
|
|
// Dimensions
|
|
readonly property int cardRadius: 16
|
|
readonly property int itemRadius: 8
|
|
readonly property int itemHeight: 60
|
|
|
|
// Accessibility
|
|
readonly property int focusRingWidth: 2
|
|
readonly property int minTargetSize: 24
|
|
}
|
|
```
|
|
|
|
**What changed:**
|
|
- `accentPurple`: `#7000FF` -> `#B794F6` (1.6:1 -> 7.2:1 contrast ratio)
|
|
- `textSecondary`: `#999999` -> `#ABABAB` (6.3:1 -> 8.1:1)
|
|
- `borderSubtle`: `rgba(1,1,1,0.08)` -> `rgba(1,1,1,0.22)` (3:1 non-text)
|
|
- New: `textDisabled`, `focusRingWidth`, `minTargetSize`
|
|
|
|
**Step 2: Verify the app launches without errors**
|
|
|
|
Run: `cd "D:/!!! SYSTEM DATA !!!/Desktop/python crap/whisper_voice" && python main.py`
|
|
|
|
Expected: App loads, Loader appears with updated accent colors. Settings opens with lighter purple accents. No QML errors in console.
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/SettingsStyle.qml
|
|
git commit -m "WCAG: Update design tokens for AAA contrast compliance"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 2: Fix ModernSlider.qml (Size + Focus Ring + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/ModernSlider.qml` (entire file, 61 lines)
|
|
|
|
**Step 1: Edit ModernSlider.qml**
|
|
|
|
Replace the entire content:
|
|
|
|
```qml
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Effects
|
|
|
|
Slider {
|
|
id: control
|
|
|
|
Accessible.role: Accessible.Slider
|
|
Accessible.name: control.value.toString()
|
|
activeFocusOnTab: true
|
|
|
|
background: Rectangle {
|
|
x: control.leftPadding
|
|
y: control.topPadding + control.availableHeight / 2 - height / 2
|
|
implicitWidth: 200
|
|
implicitHeight: 6
|
|
width: control.availableWidth
|
|
height: implicitHeight
|
|
radius: 3
|
|
color: "#2d2d3d"
|
|
|
|
Rectangle {
|
|
width: control.visualPosition * parent.width
|
|
height: parent.height
|
|
color: SettingsStyle.accent
|
|
radius: 3
|
|
}
|
|
}
|
|
|
|
handle: Item {
|
|
x: control.leftPadding + control.visualPosition * (control.availableWidth - width)
|
|
y: control.topPadding + control.availableHeight / 2 - height / 2
|
|
implicitWidth: SettingsStyle.minTargetSize
|
|
implicitHeight: SettingsStyle.minTargetSize
|
|
|
|
// Focus ring
|
|
Rectangle {
|
|
anchors.centerIn: parent
|
|
width: parent.width + SettingsStyle.focusRingWidth * 2 + 2
|
|
height: width
|
|
radius: width / 2
|
|
color: "transparent"
|
|
border.width: SettingsStyle.focusRingWidth
|
|
border.color: SettingsStyle.accent
|
|
visible: control.activeFocus
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: width / 2
|
|
color: "white"
|
|
border.color: SettingsStyle.accent
|
|
border.width: 2
|
|
|
|
layer.enabled: control.pressed
|
|
layer.effect: MultiEffect {
|
|
blurEnabled: true
|
|
blur: 0.5
|
|
shadowEnabled: true
|
|
shadowColor: SettingsStyle.accent
|
|
}
|
|
}
|
|
}
|
|
|
|
// Value Readout
|
|
Text {
|
|
anchors.right: parent.left
|
|
anchors.rightMargin: 12
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
horizontalAlignment: Text.AlignRight
|
|
|
|
text: {
|
|
var val = control.value
|
|
return (val % 1 === 0) ? val.toFixed(0) : val.toFixed(1)
|
|
}
|
|
|
|
color: SettingsStyle.textSecondary
|
|
font.family: "JetBrains Mono"
|
|
font.pixelSize: 12
|
|
font.weight: Font.Medium
|
|
}
|
|
}
|
|
```
|
|
|
|
**What changed:**
|
|
- Handle: `implicitWidth/Height: 18` -> `SettingsStyle.minTargetSize` (24px)
|
|
- Track: `implicitHeight: 4` -> `6`, radius `2` -> `3`
|
|
- Added `Accessible.role: Accessible.Slider`, `Accessible.name`
|
|
- Added `activeFocusOnTab: true`
|
|
- Added focus ring Rectangle that shows on `control.activeFocus`
|
|
- Handle is now an Item wrapping the circle + focus ring (avoids clipping)
|
|
|
|
**Step 2: Verify**
|
|
|
|
Run the app, open Settings, check sliders render with larger handles. Tab to a slider - focus ring should appear.
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/ModernSlider.qml
|
|
git commit -m "WCAG: Slider - 24px target, focus ring, accessible role"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 3: Fix ModernSwitch.qml (I/O Marks + Border + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/ModernSwitch.qml` (entire file, 40 lines)
|
|
|
|
**Step 1: Edit ModernSwitch.qml**
|
|
|
|
Replace the entire content:
|
|
|
|
```qml
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
|
|
Switch {
|
|
id: control
|
|
|
|
Accessible.role: Accessible.CheckBox
|
|
Accessible.name: control.text + (control.checked ? " on" : " off")
|
|
activeFocusOnTab: true
|
|
|
|
indicator: Rectangle {
|
|
implicitWidth: 44
|
|
implicitHeight: 24
|
|
x: control.leftPadding
|
|
y: parent.height / 2 - height / 2
|
|
radius: 12
|
|
color: control.checked ? SettingsStyle.accent : "#2d2d3d"
|
|
border.color: control.checked ? SettingsStyle.accent : SettingsStyle.borderSubtle
|
|
border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1
|
|
|
|
Behavior on color { ColorAnimation { duration: 200 } }
|
|
Behavior on border.color { ColorAnimation { duration: 200 } }
|
|
|
|
Rectangle {
|
|
x: control.checked ? parent.width - width - 3 : 3
|
|
y: 3
|
|
width: 18
|
|
height: 18
|
|
radius: 9
|
|
color: "white"
|
|
|
|
Behavior on x {
|
|
NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }
|
|
}
|
|
|
|
// I/O pip marks for non-color state indication
|
|
Text {
|
|
anchors.centerIn: parent
|
|
text: control.checked ? "I" : "O"
|
|
font.pixelSize: 9
|
|
font.bold: true
|
|
color: control.checked ? SettingsStyle.accent : "#666666"
|
|
}
|
|
}
|
|
}
|
|
|
|
contentItem: Text {
|
|
text: control.text
|
|
font: control.font
|
|
opacity: enabled ? 1.0 : 0.3
|
|
color: "white"
|
|
verticalAlignment: Text.AlignVCenter
|
|
leftPadding: control.indicator.width + control.spacing
|
|
}
|
|
}
|
|
```
|
|
|
|
**What changed:**
|
|
- Added `Accessible.role: Accessible.CheckBox` with state in name
|
|
- Added `activeFocusOnTab: true`
|
|
- Border: `"#3d3d4d"` -> `SettingsStyle.borderSubtle` token
|
|
- Focus: `border.width` switches to `focusRingWidth` on `activeFocus`
|
|
- Added "I"/"O" Text inside the thumb circle for non-color state indication
|
|
|
|
**Step 2: Verify**
|
|
|
|
Open Settings, toggle switches. "I" shows when on, "O" when off. Tab to switches - border thickens.
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/ModernSwitch.qml
|
|
git commit -m "WCAG: Switch - I/O marks, border token, focus ring, accessible role"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 4: Fix ModernTextField.qml (Placeholder + Focus + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/ModernTextField.qml` (entire file, 27 lines)
|
|
|
|
**Step 1: Edit ModernTextField.qml**
|
|
|
|
Replace the entire content:
|
|
|
|
```qml
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
|
|
TextField {
|
|
id: control
|
|
|
|
property color accentColor: "#00f2ff"
|
|
property color bgColor: "#1a1a20"
|
|
|
|
Accessible.role: Accessible.EditableText
|
|
Accessible.name: control.placeholderText || "Text input"
|
|
|
|
placeholderTextColor: SettingsStyle.textDisabled
|
|
color: "#ffffff"
|
|
font.family: "JetBrains Mono"
|
|
font.pixelSize: 14
|
|
selectedTextColor: "#000000"
|
|
selectionColor: accentColor
|
|
|
|
background: Rectangle {
|
|
implicitWidth: 200
|
|
implicitHeight: 40
|
|
color: control.bgColor
|
|
border.color: control.activeFocus ? control.accentColor : SettingsStyle.borderSubtle
|
|
border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1
|
|
radius: 6
|
|
|
|
Behavior on border.color { ColorAnimation { duration: 150 } }
|
|
}
|
|
}
|
|
```
|
|
|
|
**What changed:**
|
|
- `placeholderTextColor`: `"#606060"` -> `SettingsStyle.textDisabled` (#808080, 3.7:1)
|
|
- Border default: `"#40ffffff"` -> `SettingsStyle.borderSubtle`
|
|
- Focus `border.width`: `1` -> `SettingsStyle.focusRingWidth` (2)
|
|
- Added `Accessible.role: Accessible.EditableText`
|
|
|
|
**Step 2: Verify**
|
|
|
|
Open Settings > AI Engine > Custom Prompt field. Placeholder text should be lighter. Focus border thicker.
|
|
|
|
**Step 3: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/ModernTextField.qml
|
|
git commit -m "WCAG: TextField - placeholder contrast, focus ring, accessible role"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 5: Fix ModernComboBox.qml (Border + Arrow + Focus + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/ModernComboBox.qml:88-98` (background section)
|
|
- Modify: `src/ui/qml/ModernComboBox.qml:65-73` (indicator paint)
|
|
|
|
**Step 1: Update background border**
|
|
|
|
In `ModernComboBox.qml`, change the background Rectangle (line 88-98):
|
|
|
|
```qml
|
|
background: Rectangle {
|
|
implicitWidth: 140
|
|
implicitHeight: 40
|
|
color: control.bgColor
|
|
border.color: control.pressed || control.activeFocus ? control.accentColor : SettingsStyle.borderSubtle
|
|
border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1
|
|
radius: 6
|
|
|
|
Behavior on border.color { ColorAnimation { duration: 150 } }
|
|
}
|
|
```
|
|
|
|
**What changed:**
|
|
- Default border: `"#40ffffff"` -> `SettingsStyle.borderSubtle`
|
|
- Focus `border.width`: `1` -> `SettingsStyle.focusRingWidth` (2)
|
|
|
|
**Step 2: Update arrow indicator color**
|
|
|
|
In the `onPaint` handler (line 71), change the non-pressed fill color:
|
|
|
|
Old: `context.fillStyle = control.pressed ? control.accentColor : "#888888";`
|
|
New: `context.fillStyle = control.pressed ? control.accentColor : "#ABABAB";`
|
|
|
|
**Step 3: Add accessibility properties**
|
|
|
|
After line 11 (`property color popupColor: "#252530"`), add:
|
|
|
|
```qml
|
|
Accessible.role: Accessible.ComboBox
|
|
Accessible.name: control.displayText
|
|
```
|
|
|
|
**Step 4: Also update popup border**
|
|
|
|
In the popup background (line 115-119), change:
|
|
|
|
Old: `border.color: "#40ffffff"`
|
|
New: `border.color: SettingsStyle.borderSubtle`
|
|
|
|
**Step 5: Verify**
|
|
|
|
Open Settings, check combo boxes render with visible borders. Arrow icon is brighter.
|
|
|
|
**Step 6: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/ModernComboBox.qml
|
|
git commit -m "WCAG: ComboBox - border token, arrow contrast, focus ring, accessible role"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 6: Fix ModernKeySequenceRecorder.qml (Border + Colors + Focus + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/ModernKeySequenceRecorder.qml` (lines 10-11, 29, plus add new properties)
|
|
|
|
**Step 1: Update border color**
|
|
|
|
Line 11, change:
|
|
|
|
Old: `border.color: activeFocus || recording ? SettingsStyle.accent : "#40ffffff"`
|
|
New: `border.color: activeFocus || recording ? SettingsStyle.accent : SettingsStyle.borderSubtle`
|
|
|
|
**Step 2: Update border width for focus**
|
|
|
|
Add after `border.color` line:
|
|
|
|
Add to existing line 10: change from `border.width: 1` to:
|
|
```
|
|
border.width: (activeFocus || recording) ? SettingsStyle.focusRingWidth : 1
|
|
```
|
|
|
|
**Step 3: Update "None" text color**
|
|
|
|
Line 29, the color for empty state. Change:
|
|
|
|
Old: `color: control.recording ? SettingsStyle.accent : (control.currentSequence ? "#ffffff" : "#808080")`
|
|
New: `color: control.recording ? SettingsStyle.accent : (control.currentSequence ? "#ffffff" : "#ABABAB")`
|
|
|
|
**Step 4: Add accessibility and tab focus**
|
|
|
|
After line 3 (`Rectangle {`), inside the root Rectangle, add:
|
|
|
|
```qml
|
|
activeFocusOnTab: true
|
|
Accessible.role: Accessible.Button
|
|
Accessible.name: control.currentSequence ? "Hotkey: " + control.currentSequence + ". Click to change" : "No hotkey set. Click to record"
|
|
```
|
|
|
|
Note: `activeFocusOnTab: true` is critical since the recorder uses `forceActiveFocus()` on click but needs to also be reachable by Tab.
|
|
|
|
**Step 5: Verify**
|
|
|
|
Open Settings > General tab. Hotkey recorders should have visible borders, "None" text brighter. Tab to recorders.
|
|
|
|
**Step 6: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/ModernKeySequenceRecorder.qml
|
|
git commit -m "WCAG: KeySequenceRecorder - border token, focus ring, accessible role"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 7: Fix GlowButton.qml (Text Color + Border + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/GlowButton.qml` (lines 14, 28, plus add new properties)
|
|
|
|
**Step 1: Update default text color**
|
|
|
|
Line 14, change:
|
|
|
|
Old: `color: control.hovered ? "white" : "#9499b0"`
|
|
New: `color: control.hovered ? "white" : "#ABABAB"`
|
|
|
|
**Step 2: Update default border color**
|
|
|
|
Line 28, change:
|
|
|
|
Old: `border.color: control.hovered ? control.accentColor : Qt.rgba(1, 1, 1, 0.1)`
|
|
New: `border.color: control.hovered ? control.accentColor : SettingsStyle.borderSubtle`
|
|
|
|
**Step 3: Add accessibility and focus ring**
|
|
|
|
After line 8 (`property color accentColor: "#00f2ff"`), add:
|
|
|
|
```qml
|
|
Accessible.role: Accessible.Button
|
|
Accessible.name: control.text
|
|
activeFocusOnTab: true
|
|
```
|
|
|
|
Also update the background `border.width` (line 29):
|
|
|
|
Old: `border.width: 1`
|
|
New: `border.width: control.activeFocus ? SettingsStyle.focusRingWidth : 1`
|
|
|
|
**Step 4: Verify**
|
|
|
|
Check any GlowButton instances in the app (Download button uses Button directly, so GlowButton may only appear if used elsewhere — verify usage).
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/GlowButton.qml
|
|
git commit -m "WCAG: GlowButton - text contrast, border token, focus ring, accessible role"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 8: Fix ModernSettingsSection.qml + ModernSettingsItem.qml (Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/ModernSettingsSection.qml` (add 2 lines)
|
|
- Modify: `src/ui/qml/ModernSettingsItem.qml` (add 2 lines)
|
|
|
|
**Step 1: Add accessibility to ModernSettingsSection**
|
|
|
|
After line 8 (`property string title: ""`), add:
|
|
|
|
```qml
|
|
Accessible.name: root.title + " settings group"
|
|
Accessible.role: Accessible.Grouping
|
|
```
|
|
|
|
**Step 2: Add accessibility to ModernSettingsItem**
|
|
|
|
After line 20 (`property bool showSeparator: true`), add:
|
|
|
|
```qml
|
|
Accessible.name: root.label
|
|
Accessible.role: Accessible.Row
|
|
```
|
|
|
|
**Step 3: Verify**
|
|
|
|
Open Settings. Use Windows Narrator or Accessibility Insights to confirm sections and items are announced with their labels.
|
|
|
|
**Step 4: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/ModernSettingsSection.qml src/ui/qml/ModernSettingsItem.qml
|
|
git commit -m "WCAG: Add accessible name/role to settings sections and items"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 9: Fix Settings.qml (Hardcoded Colors + Keyboard Nav + Accessibility)
|
|
|
|
This is the largest file (1116 lines). Multiple targeted edits.
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/Settings.qml`
|
|
|
|
**Step 1: Update window title**
|
|
|
|
Line 15, change:
|
|
|
|
Old: `title: "Settings"`
|
|
New: `title: "WhisperVoice Settings"`
|
|
|
|
**Step 2: Add window-level accessibility**
|
|
|
|
After line 15, add:
|
|
|
|
```qml
|
|
Accessible.name: "WhisperVoice Settings"
|
|
```
|
|
|
|
**Step 3: Fix close button hover colors**
|
|
|
|
Line 137, change:
|
|
|
|
Old: `color: closeMa.containsMouse ? "#20ff4b4b" : "transparent"`
|
|
New: `color: closeMa.containsMouse ? "#20FF8A8A" : "transparent"`
|
|
|
|
Line 138, change:
|
|
|
|
Old: `border.color: closeMa.containsMouse ? "#40ff4b4b" : "transparent"`
|
|
New: `border.color: closeMa.containsMouse ? "#40FF8A8A" : "transparent"`
|
|
|
|
Line 144, change:
|
|
|
|
Old: `color: closeMa.containsMouse ? "#ff4b4b" : SettingsStyle.textSecondary`
|
|
New: `color: closeMa.containsMouse ? "#FF8A8A" : SettingsStyle.textSecondary`
|
|
|
|
**Step 4: Add close button keyboard support and accessibility**
|
|
|
|
After the close button Rectangle opening (line 134), add:
|
|
|
|
```qml
|
|
activeFocusOnTab: true
|
|
Accessible.name: "Close settings"
|
|
Accessible.role: Accessible.Button
|
|
Keys.onReturnPressed: root.close()
|
|
Keys.onSpacePressed: root.close()
|
|
```
|
|
|
|
Add a focus indicator. After `Behavior on border.color` (line 159), add:
|
|
|
|
```qml
|
|
// Focus ring
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: 8
|
|
color: "transparent"
|
|
border.width: SettingsStyle.focusRingWidth
|
|
border.color: SettingsStyle.accent
|
|
visible: parent.activeFocus
|
|
}
|
|
```
|
|
|
|
**Step 5: Fix nav sidebar keyboard support**
|
|
|
|
In the Repeater delegate (line 203), after `radius: 6`, add:
|
|
|
|
```qml
|
|
activeFocusOnTab: true
|
|
Accessible.name: name
|
|
Accessible.role: Accessible.Tab
|
|
Keys.onReturnPressed: stack.currentIndex = index
|
|
Keys.onSpacePressed: stack.currentIndex = index
|
|
Keys.onDownPressed: {
|
|
if (index < navModel.count - 1) {
|
|
// Move focus to next nav item
|
|
var nextItem = parent.children[index + 1]
|
|
if (nextItem) nextItem.forceActiveFocus()
|
|
}
|
|
}
|
|
Keys.onUpPressed: {
|
|
if (index > 0) {
|
|
var prevItem = parent.children[index - 1]
|
|
if (prevItem) prevItem.forceActiveFocus()
|
|
}
|
|
}
|
|
```
|
|
|
|
Add focus indicator inside the nav delegate, after the MouseArea (line 258):
|
|
|
|
```qml
|
|
// Focus ring
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
radius: 6
|
|
color: "transparent"
|
|
border.width: SettingsStyle.focusRingWidth
|
|
border.color: SettingsStyle.accent
|
|
visible: parent.activeFocus
|
|
}
|
|
```
|
|
|
|
**Step 6: Fix StatBox accent colors**
|
|
|
|
Line 1068, change:
|
|
|
|
Old: `StatBox { label: "APP RAM"; value: ui.appRamMb; unit: "MB"; accent: "#bd93f9" }`
|
|
New: `StatBox { label: "APP RAM"; value: ui.appRamMb; unit: "MB"; accent: "#CAA9FF" }`
|
|
|
|
Line 1069, change:
|
|
|
|
Old: `StatBox { label: "GPU VRAM"; value: ui.appVramMb; unit: "MB"; accent: "#ff79c6" }`
|
|
New: `StatBox { label: "GPU VRAM"; value: ui.appVramMb; unit: "MB"; accent: "#FF8FD0" }`
|
|
|
|
Line 1070, change:
|
|
|
|
Old: `StatBox { label: "GPU LOAD"; value: ui.appVramPercent; unit: "%"; accent: "#ff5555" }`
|
|
New: `StatBox { label: "GPU LOAD"; value: ui.appVramPercent; unit: "%"; accent: "#FF8A8A" }`
|
|
|
|
**Step 7: Fix model info text**
|
|
|
|
Line 755, change:
|
|
|
|
Old: `font.pixelSize: 10`
|
|
New: `font.pixelSize: 11`
|
|
|
|
Line 756, change (remove opacity):
|
|
|
|
Old: `opacity: 0.7`
|
|
New: `opacity: 1.0`
|
|
|
|
**Step 8: Verify**
|
|
|
|
Open Settings. Check:
|
|
- Close button hover is softer red (#FF8A8A)
|
|
- StatBox accents are lighter pastels
|
|
- Model info text is readable without opacity reduction
|
|
- Tab through nav items, close button
|
|
- Arrow keys move between nav items
|
|
|
|
**Step 9: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/Settings.qml
|
|
git commit -m "WCAG: Settings - AAA colors, keyboard nav, accessible roles"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 10: Fix Overlay.qml (Border + Keyboard + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/Overlay.qml`
|
|
|
|
**Step 1: Add window-level accessibility**
|
|
|
|
After line 15 (`color: "transparent"`), add:
|
|
|
|
```qml
|
|
title: "WhisperVoice"
|
|
Accessible.name: "WhisperVoice Overlay"
|
|
```
|
|
|
|
**Step 2: Fix border color**
|
|
|
|
Line 175, change:
|
|
|
|
Old: `border.color: "#40ffffff"`
|
|
New: `border.color: Qt.rgba(1, 1, 1, 0.22)`
|
|
|
|
**Step 3: Add mic button keyboard support and accessibility**
|
|
|
|
In the `micContainer` Item (line 206), after `anchors.verticalCenter: parent.verticalCenter`, add:
|
|
|
|
```qml
|
|
activeFocusOnTab: true
|
|
Accessible.name: ui.isRecording ? "Stop recording" : "Start recording"
|
|
Accessible.role: Accessible.Button
|
|
Keys.onReturnPressed: ui.toggleRecordingRequested()
|
|
Keys.onSpacePressed: ui.toggleRecordingRequested()
|
|
```
|
|
|
|
**Step 4: Add focus ring to mic button**
|
|
|
|
Inside micContainer, add a focus indicator after the micCircle Rectangle (after line 265):
|
|
|
|
```qml
|
|
// Focus ring
|
|
Rectangle {
|
|
anchors.fill: micCircle
|
|
anchors.margins: -SettingsStyle.focusRingWidth - 1
|
|
radius: width / 2
|
|
color: "transparent"
|
|
border.width: SettingsStyle.focusRingWidth
|
|
border.color: SettingsStyle.accent
|
|
visible: micContainer.activeFocus
|
|
}
|
|
```
|
|
|
|
Note: This requires importing SettingsStyle. Check if it's already available in Overlay.qml — it likely isn't since Overlay uses hardcoded values. If SettingsStyle is not importable in Overlay.qml, use a literal `2` for border width and `"#B794F6"` for color.
|
|
|
|
**Step 5: Add accessibility to recording timer text**
|
|
|
|
In the timer Text (around line 333), add:
|
|
|
|
```qml
|
|
Accessible.role: Accessible.StaticText
|
|
Accessible.name: "Recording time: " + text
|
|
```
|
|
|
|
**Step 6: Verify**
|
|
|
|
Launch app, trigger recording. Border should be more visible. Tab to mic button - focus ring appears. Press Enter on focused mic button - recording toggles.
|
|
|
|
**Step 7: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/Overlay.qml
|
|
git commit -m "WCAG: Overlay - border contrast, keyboard nav, accessible roles"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 11: Fix Loader.qml (Colors + Accessibility)
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/Loader.qml`
|
|
|
|
**Step 1: Add window-level accessibility**
|
|
|
|
After line 16 (`color: "transparent"`), add:
|
|
|
|
```qml
|
|
title: "WhisperVoice"
|
|
Accessible.name: "WhisperVoice Loading"
|
|
```
|
|
|
|
**Step 2: Fix border color**
|
|
|
|
Line 24, change:
|
|
|
|
Old: `border.color: "#40ffffff"`
|
|
New: `border.color: Qt.rgba(1, 1, 1, 0.22)`
|
|
|
|
**Step 3: Fix subtitle color**
|
|
|
|
Line 98, change:
|
|
|
|
Old: `color: "#80ffffff"`
|
|
New: `color: "#ABABAB"`
|
|
|
|
**Step 4: Fix status text opacity**
|
|
|
|
Line 161, change:
|
|
|
|
Old: `opacity: 0.8`
|
|
New: (remove the line entirely, or set to 1.0)
|
|
|
|
**Step 5: Add status text accessibility**
|
|
|
|
After line 159 (`font.bold: true`), add:
|
|
|
|
```qml
|
|
Accessible.role: Accessible.StaticText
|
|
Accessible.name: "Loading status: " + text
|
|
```
|
|
|
|
**Step 6: Verify**
|
|
|
|
Launch app. Loader subtitle "AI TRANSCRIPTION ENGINE" should be brighter. Status text fully opaque. Border more visible.
|
|
|
|
**Step 7: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/Loader.qml
|
|
git commit -m "WCAG: Loader - subtitle contrast, status opacity, accessible roles"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 12: Add Reduced Motion Support (Python Backend)
|
|
|
|
**Files:**
|
|
- Modify: `src/core/config.py:61` (add default)
|
|
- Modify: `src/ui/bridge.py` (add property)
|
|
- Modify: `main.py` (add OS detection)
|
|
|
|
**Step 1: Add config default**
|
|
|
|
In `src/core/config.py`, add to DEFAULT_SETTINGS dict, after line 61 (`"unload_models_after_use": False`):
|
|
|
|
```python
|
|
"unload_models_after_use": False,
|
|
|
|
# Accessibility
|
|
"reduce_motion": False # Disable animations for WCAG 2.3.3
|
|
```
|
|
|
|
(Note: change the comma on the `unload_models_after_use` line too)
|
|
|
|
**Step 2: Add bridge property**
|
|
|
|
In `src/ui/bridge.py`, add a new signal in the UIBridge class signals section (after line 113):
|
|
|
|
```python
|
|
reduceMotionChanged = Signal(bool)
|
|
```
|
|
|
|
In `__init__` (after line 132, `self._is_destroyed = False`), add:
|
|
|
|
```python
|
|
self._reduce_motion = bool(ConfigManager().get("reduce_motion"))
|
|
```
|
|
|
|
Add the property (after the `uiScale` property block, around line 289):
|
|
|
|
```python
|
|
@Property(bool, notify=reduceMotionChanged)
|
|
def reduceMotion(self): return self._reduce_motion
|
|
|
|
@reduceMotion.setter
|
|
def reduceMotion(self, val):
|
|
if self._reduce_motion != val:
|
|
self._reduce_motion = val
|
|
self.reduceMotionChanged.emit(val)
|
|
```
|
|
|
|
Also update `setSetting` (around line 275-280) to handle `reduce_motion`:
|
|
|
|
After the `if key == "ui_scale":` block, add:
|
|
|
|
```python
|
|
if key == "reduce_motion":
|
|
self.reduceMotion = bool(value)
|
|
```
|
|
|
|
**Step 3: Add Windows OS detection in main.py**
|
|
|
|
After the resolution detection block (after line 81, before `# Configure Logging`), add:
|
|
|
|
```python
|
|
# Detect Windows "Reduce Motion" preference
|
|
try:
|
|
import ctypes
|
|
SPI_GETCLIENTAREAANIMATION = 0x1042
|
|
animation_enabled = ctypes.c_bool(True)
|
|
ctypes.windll.user32.SystemParametersInfoW(
|
|
SPI_GETCLIENTAREAANIMATION, 0,
|
|
ctypes.byref(animation_enabled), 0
|
|
)
|
|
if not animation_enabled.value:
|
|
# OS says reduce motion — set as default (user can override in Settings)
|
|
ConfigManager().data["reduce_motion"] = True
|
|
ConfigManager().save()
|
|
except Exception:
|
|
pass
|
|
```
|
|
|
|
**Step 4: Verify**
|
|
|
|
Run the app. Check `ui.reduceMotion` is accessible from QML. No crashes.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/core/config.py src/ui/bridge.py main.py
|
|
git commit -m "WCAG: Add reduce_motion config, bridge property, OS detection"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 13: Apply Reduced Motion Conditionals to Overlay.qml
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/Overlay.qml`
|
|
|
|
**Step 1: Shimmer animation**
|
|
|
|
Line 38-39, change:
|
|
|
|
Old: `running: true`
|
|
New: `running: !ui.reduceMotion`
|
|
|
|
**Step 2: Gradient blobs shader**
|
|
|
|
Line 96-102, wrap the NumberAnimation. Change:
|
|
|
|
Old: `NumberAnimation on time { from: 0; to: 1000; duration: 100000; loops: Animation.Infinite }`
|
|
|
|
Add `visible` to the ShaderEffect (line 96):
|
|
|
|
After `opacity: 0.4` (line 98), add:
|
|
```qml
|
|
visible: !ui.reduceMotion
|
|
```
|
|
|
|
**Step 3: Glow shader**
|
|
|
|
After `opacity: 0.04` (line 107), add:
|
|
```qml
|
|
visible: !ui.reduceMotion
|
|
```
|
|
|
|
**Step 4: Particles**
|
|
|
|
On the ParticleSystem (line 115), add:
|
|
```qml
|
|
running: !ui.reduceMotion
|
|
```
|
|
|
|
**Step 5: CRT shader**
|
|
|
|
On the CRT ShaderEffect (line 144), add:
|
|
```qml
|
|
visible: !ui.reduceMotion
|
|
```
|
|
|
|
**Step 6: Rainbow wave shader**
|
|
|
|
On the ShaderEffect inside waveformContainer (line 278), add:
|
|
```qml
|
|
visible: !ui.reduceMotion
|
|
```
|
|
|
|
**Step 7: Mic button pulse animation**
|
|
|
|
Line 247-252 (SequentialAnimation on scale), change:
|
|
|
|
Old: `running: ui.isRecording`
|
|
New: `running: ui.isRecording && !ui.reduceMotion`
|
|
|
|
**Step 8: Timer text blink animation**
|
|
|
|
Line 344-348 (SequentialAnimation on opacity), change:
|
|
|
|
Old: `running: ui.isRecording; loops: Animation.Infinite`
|
|
New: `running: ui.isRecording && !ui.reduceMotion; loops: Animation.Infinite`
|
|
|
|
**Step 9: Recording border pulse**
|
|
|
|
Line 196-201 (SequentialAnimation on border.color), change:
|
|
|
|
Old: `running: ui.isRecording`
|
|
New: `running: ui.isRecording && !ui.reduceMotion`
|
|
|
|
**Step 10: Verify**
|
|
|
|
Set `reduce_motion: true` in settings.json manually. Launch app. Overlay should be static (no shimmer, no blobs, no glow, no particles, no CRT, no rainbow). Still functional — mic button works, timer shows.
|
|
|
|
**Step 11: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/Overlay.qml
|
|
git commit -m "WCAG: Overlay - conditional animations for reduced motion"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 14: Apply Reduced Motion to Loader.qml + Add Settings Toggle
|
|
|
|
**Files:**
|
|
- Modify: `src/ui/qml/Loader.qml`
|
|
- Modify: `src/ui/qml/Settings.qml`
|
|
|
|
**Step 1: Loader icon pulse**
|
|
|
|
In Loader.qml, line 57-61 (SequentialAnimation on scale), change:
|
|
|
|
Old: `loops: Animation.Infinite`
|
|
|
|
Wrap it — add before the animation:
|
|
```qml
|
|
running: ui ? !ui.reduceMotion : true
|
|
```
|
|
|
|
(Use ternary because Loader loads before bridge might be ready)
|
|
|
|
**Step 2: Loader progress shimmer**
|
|
|
|
Line 144-148 (NumberAnimation on x), wrap the parent Rectangle. Add to the shimmer Rectangle (line 136):
|
|
|
|
```qml
|
|
visible: ui ? !ui.reduceMotion : true
|
|
```
|
|
|
|
**Step 3: Add "Reduce Motion" toggle in Settings Visuals tab**
|
|
|
|
In Settings.qml, find the Visuals tab Overlay section. After the "Window Opacity" `ModernSettingsItem` (around line 520-530, the one with `showSeparator: false`), change `showSeparator: false` to `showSeparator: true` on the opacity item, then add a new item after it:
|
|
|
|
```qml
|
|
ModernSettingsItem {
|
|
label: "Reduce Motion"
|
|
description: "Disable animations for accessibility"
|
|
showSeparator: false
|
|
control: ModernSwitch {
|
|
checked: ui.getSetting("reduce_motion")
|
|
onToggled: ui.setSetting("reduce_motion", checked)
|
|
}
|
|
}
|
|
```
|
|
|
|
**Step 4: Verify**
|
|
|
|
Launch app. Open Settings > Visuals. Toggle "Reduce Motion" on. Overlay animations should stop. Loader shimmer should stop on next launch with setting persisted.
|
|
|
|
**Step 5: Commit**
|
|
|
|
```bash
|
|
git add src/ui/qml/Loader.qml src/ui/qml/Settings.qml
|
|
git commit -m "WCAG: Reduced motion toggle in Visuals, conditionals in Loader"
|
|
```
|
|
|
|
---
|
|
|
|
### Task 15: Final Verification and Cleanup
|
|
|
|
**Step 1: Full visual verification**
|
|
|
|
Run the app end-to-end:
|
|
|
|
```bash
|
|
cd "D:/!!! SYSTEM DATA !!!/Desktop/python crap/whisper_voice" && python main.py
|
|
```
|
|
|
|
Checklist:
|
|
- [ ] Loader appears with brighter subtitle, no opacity on status
|
|
- [ ] Overlay border is visible
|
|
- [ ] Settings opens with lighter purple accent (#B794F6)
|
|
- [ ] All text is readable (no low-contrast text)
|
|
- [ ] Switch shows I/O marks
|
|
- [ ] Slider handles are 24px
|
|
- [ ] Tab key moves through all interactive controls
|
|
- [ ] Enter/Space activates focused controls
|
|
- [ ] Reduce Motion toggle works (Settings > Visuals)
|
|
- [ ] StatBox colors in Debug tab are lighter pastels
|
|
- [ ] No QML warnings in console output
|
|
|
|
**Step 2: Commit any remaining fixes**
|
|
|
|
If any issues found, fix and commit individually.
|
|
|
|
**Step 3: Final commit**
|
|
|
|
```bash
|
|
git add -A
|
|
git commit -m "WCAG 2.2 AAA: Full compliance implementation complete"
|
|
```
|