diff --git a/.Jules/palette.md b/.Jules/palette.md index f61603c8..9632a4ec 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,27 +1,3 @@ -## 2024-06-03 - Missing tooltips and accessibility on list item actions -**Learning:** Icon-only buttons used for item actions (like 'Edit', 'Delete', 'Remove') within list views often lack tooltips and accessibility labels. This omission makes the UI ambiguous for mouse users on macOS and inaccessible to screen readers. -**Action:** Always add `.help("Action Name")` and `.accessibilityLabel("Action Name Item Type")` to icon-only buttons, especially those located at the trailing edge of list rows. - -## 2024-11-20 - Ensure Symmetry in Accessibility and Tooltips for SwiftUI Icon Buttons -**Learning:** In macOS SwiftUI applications, icon-only buttons often have either `.help()` (for hover tooltips) or `.accessibilityLabel()` (for VoiceOver), but frequently lack both. Both are necessary because they serve different user interaction models—`.help` for visual hover feedback and `.accessibilityLabel` for screen readers. -**Action:** When adding or reviewing icon-only buttons in SwiftUI, always ensure symmetry by defining both `.help("Description")` and `.accessibilityLabel("Description")` to cover all accessibility and usability vectors. -## 2026-06-06 - Ensure Symmetry in Accessibility and Tooltips for SwiftUI Icon Buttons -**Learning:** In macOS SwiftUI applications, icon-only buttons often have either `.help()` (for hover tooltips) or `.accessibilityLabel()` (for VoiceOver), but frequently lack both. Both are necessary because they serve different user interaction models—`.help` for visual hover feedback and `.accessibilityLabel` for screen readers. -**Action:** When adding or reviewing icon-only buttons in SwiftUI, always ensure symmetry by defining both `.help("Description")` and `.accessibilityLabel("Description")` to cover all accessibility and usability vectors. - -## 2024-05-19 - [Missing Accessibility on Dynamic Form Dictionary Buttons] -**Learning:** Icon-only buttons used for adding/removing items in dynamic dictionary forms (e.g., webhook headers with `plus.circle.fill` and `minus.circle.fill`) are frequent vectors for missing accessibility labels and tooltips, because they are often implemented with plain button styles without considering screen reader context. -**Action:** Always verify that inline addition/removal buttons in repeated form elements have explicit `.help()` and `.accessibilityLabel()` modifiers attached to them to ensure they can be understood and navigated accurately by all users. -## 2024-05-14 - [Icon Button Accessibility Gap] -**Learning:** In standard SwiftUI development for macOS, icon-only buttons (`Image(systemName: ...)`) are frequently missing `.help()` (for hover tooltips) and `.accessibilityLabel()` (for VoiceOver). Several core navigation components (like Layer tabs and Sidebar buttons) suffered from this pattern. -**Action:** Always verify that every `Button` wrapping an `Image` has both `.help()` and `.accessibilityLabel()` strings defined, unless explicitly marked as decorative. - -## 2024-06-08 - Ensure Symmetry in Accessibility and Tooltips for SwiftUI Icon Buttons -**Learning:** Verified the necessity of ensuring full symmetry in accessibility and tooltips for icon-only buttons (`Image(systemName: ...)`). These buttons often get one but not the other (`.help()` without `.accessibilityLabel()`, or vice-versa), which causes poor UX for some group of users. -**Action:** When adding or modifying icon buttons, always apply both `.help("Action")` and `.accessibilityLabel("Action")`. -## 2026-06-16 - Ensure Symmetry in Accessibility and Tooltips for SwiftUI Icon Buttons -**Learning:** Verified the necessity of ensuring full symmetry in accessibility and tooltips for icon-only buttons (`Image(systemName: ...)`). These buttons often get one but not the other (`.help()` without `.accessibilityLabel()`, or vice-versa), which causes poor UX for some group of users. -**Action:** When adding or modifying icon buttons, always apply both `.help("Action")` and `.accessibilityLabel("Action")`. -## 2026-06-23 - [SwiftUI Button Accessibility] -**Learning:** Found a pattern where SwiftUI icon-only buttons or minimal UI elements were given `.help()` modifiers for hover tooltips but lacked `.accessibilityLabel()` modifiers for screen readers. -**Action:** When adding `.help()` to buttons, always pair it with a corresponding `.accessibilityLabel()` to ensure full accessibility. +## 2024-06-25 - [Accessibility Context in Lists] +**Learning:** In repeated lists or loops, icon-only buttons with generic `.accessibilityLabel()` or `.help()` (like "Edit" or "Delete") lose context, confusing screen reader users. +**Action:** Always inject dynamic context from the current item (e.g., `item.displayName`) into the accessibility label and help modifiers to disambiguate the action (e.g., "Edit Home Button"). diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordSequenceListViews.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordSequenceListViews.swift index 169053d5..412a3058 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordSequenceListViews.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ChordSequenceListViews.swift @@ -115,16 +115,16 @@ struct ChordRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit") - .accessibilityLabel("Edit") + .help("Edit \(chord.actionDisplayString)") + .accessibilityLabel("Edit \(chord.actionDisplayString)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete") - .accessibilityLabel("Delete") + .help("Delete \(chord.actionDisplayString)") + .accessibilityLabel("Delete \(chord.actionDisplayString)") } } .padding(.horizontal, 12) @@ -261,16 +261,16 @@ struct SequenceRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit") - .accessibilityLabel("Edit") + .help("Edit \(sequence.actionDisplayString)") + .accessibilityLabel("Edit \(sequence.actionDisplayString)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete") - .accessibilityLabel("Delete") + .help("Delete \(sequence.actionDisplayString)") + .accessibilityLabel("Delete \(sequence.actionDisplayString)") } } .padding(.horizontal, 12) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/CommandWheelSettingsView.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/CommandWheelSettingsView.swift index 02bd24c3..4cf73165 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/CommandWheelSettingsView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/CommandWheelSettingsView.swift @@ -176,8 +176,8 @@ struct CommandWheelActionRow: View { } .buttonStyle(.plain) .foregroundColor(.secondary) - .help("Edit") - .accessibilityLabel("Edit Command Wheel Action") + .help("Edit \(action.displayName.isEmpty ? "Unnamed" : action.displayName)") + .accessibilityLabel("Edit \(action.displayName.isEmpty ? "Unnamed" : action.displayName)") // Delete button Button(action: onDelete) { @@ -186,8 +186,8 @@ struct CommandWheelActionRow: View { } .buttonStyle(.plain) .foregroundColor(.secondary) - .help("Delete") - .accessibilityLabel("Delete Command Wheel Action") + .help("Delete \(action.displayName.isEmpty ? "Unnamed" : action.displayName)") + .accessibilityLabel("Delete \(action.displayName.isEmpty ? "Unnamed" : action.displayName)") } .padding(.vertical, 4) .padding(.horizontal, 6) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/GestureListViews.swift b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/GestureListViews.swift index a77232ea..ed85a992 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/GestureListViews.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/GestureListViews.swift @@ -78,8 +78,8 @@ struct GestureRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit") - .accessibilityLabel("Edit") + .help("Edit \(gestureType.displayName)") + .accessibilityLabel("Edit \(gestureType.displayName)") if mapping?.hasAction == true { Button(action: onClear) { @@ -87,8 +87,8 @@ struct GestureRow: View { .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Clear") - .accessibilityLabel("Clear") + .help("Clear \(gestureType.displayName)") + .accessibilityLabel("Clear \(gestureType.displayName)") } } }