Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@
## 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-05-18 - [Accessibility context in generic list rows]
**Learning:** Found that generic icon-only buttons (like Edit/Delete) inside lists lacked specific context for screen readers. Using variables from the list loop in string interpolation (`"Edit \(item.name)"`) makes them significantly more navigable.
**Action:** Always include item-specific properties inside `accessibilityLabel` modifiers on buttons inside `ForEach` loops, and ensure fallbacks exist for empty string cases (e.g. `macro.name.isEmpty ? "Unnamed Macro" : macro.name`).
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ struct SharedMacroRow: View {
}
.buttonStyle(.borderless)
.help("Edit in shared library")
.accessibilityLabel("Edit in shared library")
.accessibilityLabel("Edit \(macro.name.isEmpty ? "Unnamed Macro" : macro.name) in shared library")
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
Expand Down Expand Up @@ -207,15 +207,15 @@ struct MacroRow: View {
}
.buttonStyle(.borderless)
.help("Edit")
.accessibilityLabel("Edit")
.accessibilityLabel("Edit \(macro.name.isEmpty ? "Unnamed Macro" : macro.name)")

Button(action: onDelete) {
Image(systemName: "trash")
.foregroundColor(.red.opacity(0.8))
}
.buttonStyle(.borderless)
.help("Delete")
.accessibilityLabel("Delete")
.accessibilityLabel("Delete \(macro.name.isEmpty ? "Unnamed Macro" : macro.name)")
}
}
.padding(.horizontal, 12)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ struct ChordRow: View {
}
.buttonStyle(.borderless)
.help("Edit")
.accessibilityLabel("Edit")
.accessibilityLabel("Edit \(chord.actionDisplayString.isEmpty ? "Unnamed Chord" : chord.actionDisplayString)")

Button(action: onDelete) {
Image(systemName: "trash")
.foregroundColor(.red.opacity(0.8))
}
.buttonStyle(.borderless)
.help("Delete")
.accessibilityLabel("Delete")
.accessibilityLabel("Delete \(chord.actionDisplayString.isEmpty ? "Unnamed Chord" : chord.actionDisplayString)")
}
}
.padding(.horizontal, 12)
Expand Down Expand Up @@ -262,15 +262,15 @@ struct SequenceRow: View {
}
.buttonStyle(.borderless)
.help("Edit")
.accessibilityLabel("Edit")
.accessibilityLabel("Edit \(sequence.actionDisplayString.isEmpty ? "Unnamed Sequence" : sequence.actionDisplayString)")

Button(action: onDelete) {
Image(systemName: "trash")
.foregroundColor(.red.opacity(0.8))
}
.buttonStyle(.borderless)
.help("Delete")
.accessibilityLabel("Delete")
.accessibilityLabel("Delete \(sequence.actionDisplayString.isEmpty ? "Unnamed Sequence" : sequence.actionDisplayString)")
}
}
.padding(.horizontal, 12)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ struct GestureRow: View {
}
.buttonStyle(.borderless)
.help("Edit")
.accessibilityLabel("Edit")
.accessibilityLabel("Edit \(gestureType.displayName)")

if mapping?.hasAction == true {
Button(action: onClear) {
Expand All @@ -88,7 +88,7 @@ struct GestureRow: View {
}
.buttonStyle(.borderless)
.help("Clear")
.accessibilityLabel("Clear")
.accessibilityLabel("Clear \(gestureType.displayName)")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ struct LinkedAppsSheet: View {
} else {
ForEach(liveProfile.linkedApps, id: \.self) { bundleId in
HStack {
if let appInfo = appMonitor.appInfo(for: bundleId) {
let appInfo = appMonitor.appInfo(for: bundleId)
if let appInfo = appInfo {
if let icon = appInfo.icon {
Image(nsImage: icon)
.resizable()
Expand All @@ -58,7 +59,7 @@ struct LinkedAppsSheet: View {
}
.buttonStyle(.borderless)
.help("Remove")
.accessibilityLabel("Remove Linked App")
.accessibilityLabel("Remove \(appInfo?.name ?? bundleId)")
}
.padding(.vertical, 4)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ struct LinkedControllersSheet: View {
}
.buttonStyle(.borderless)
.help("Remove")
.accessibilityLabel("Remove Linked Controller")
.accessibilityLabel("Remove \(binding.displayName.isEmpty ? "Unnamed Controller" : binding.displayName)")
}
.padding(.vertical, 4)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,15 @@ struct ScriptRow: View {
}
.buttonStyle(.borderless)
.help("Edit")
.accessibilityLabel("Edit")
.accessibilityLabel("Edit \(script.name.isEmpty ? "Unnamed Script" : script.name)")

Button(action: onDelete) {
Image(systemName: "trash")
.foregroundColor(.red.opacity(0.8))
}
.buttonStyle(.borderless)
.help("Delete")
.accessibilityLabel("Delete")
.accessibilityLabel("Delete \(script.name.isEmpty ? "Unnamed Script" : script.name)")
}
}
.padding(.horizontal, 12)
Expand Down
Loading