From da7790e01ae90aaeec5167535b3e7a19dacb0e96 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:07:13 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Dynamic=20accessi?= =?UTF-8?q?bility=20labels=20in=20lists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .Jules/palette.md | 4 ++++ .../Views/Macros/MacroListView.swift | 12 ++++++------ .../Views/Scripts/ScriptListView.swift | 8 ++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index f61603c8..ad4c14de 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -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-07-13 - Dynamic Accessibility Labels in Lists +**Learning:** Using static labels like "Edit" or "Delete" on icon-only buttons inside lists makes them ambiguous for screen reader users (e.g., hearing "Edit, Edit, Edit"). +**Action:** Always inject contextual names (e.g., `macro.name` or `script.name`) into both `.help()` and `.accessibilityLabel()` for repeated list actions to disambiguate the target. Ensure a fallback exists if the name is empty (e.g., "Untitled"). diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift index 211ecb1e..7ce6a350 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift @@ -155,8 +155,8 @@ struct SharedMacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit in shared library") - .accessibilityLabel("Edit in shared library") + .help("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name) in shared library") + .accessibilityLabel("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name) in shared library") } .padding(.horizontal, 12) .padding(.vertical, 8) @@ -206,16 +206,16 @@ struct MacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit") - .accessibilityLabel("Edit") + .help("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name)") + .accessibilityLabel("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete") - .accessibilityLabel("Delete") + .help("Delete \(macro.name.isEmpty ? "Unnamed" : macro.name)") + .accessibilityLabel("Delete \(macro.name.isEmpty ? "Unnamed" : macro.name)") } } .padding(.horizontal, 12) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift index 40c6f5ff..11c8dd28 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift @@ -222,16 +222,16 @@ struct ScriptRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit") - .accessibilityLabel("Edit") + .help("Edit \(script.name.isEmpty ? "Untitled Script" : script.name)") + .accessibilityLabel("Edit \(script.name.isEmpty ? "Untitled Script" : script.name)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete") - .accessibilityLabel("Delete") + .help("Delete \(script.name.isEmpty ? "Untitled Script" : script.name)") + .accessibilityLabel("Delete \(script.name.isEmpty ? "Untitled Script" : script.name)") } } .padding(.horizontal, 12) From 8bf70e6a0dc5c3207891faf2a72a3f89f25fbfc1 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:29:54 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Dynamic=20accessi?= =?UTF-8?q?bility=20labels=20in=20lists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .Jules/palette.md | 4 ---- .../Views/Macros/MacroListView.swift | 12 ++++++------ .../Views/Scripts/ScriptListView.swift | 8 ++++---- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index ad4c14de..f61603c8 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -25,7 +25,3 @@ ## 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-07-13 - Dynamic Accessibility Labels in Lists -**Learning:** Using static labels like "Edit" or "Delete" on icon-only buttons inside lists makes them ambiguous for screen reader users (e.g., hearing "Edit, Edit, Edit"). -**Action:** Always inject contextual names (e.g., `macro.name` or `script.name`) into both `.help()` and `.accessibilityLabel()` for repeated list actions to disambiguate the target. Ensure a fallback exists if the name is empty (e.g., "Untitled"). diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift index 7ce6a350..87668c58 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift @@ -155,8 +155,8 @@ struct SharedMacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name) in shared library") - .accessibilityLabel("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name) in shared library") + .help("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name) in shared library") + .accessibilityLabel("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name) in shared library") } .padding(.horizontal, 12) .padding(.vertical, 8) @@ -206,16 +206,16 @@ struct MacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name)") - .accessibilityLabel("Edit \(macro.name.isEmpty ? "Unnamed" : macro.name)") + .help("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") + .accessibilityLabel("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete \(macro.name.isEmpty ? "Unnamed" : macro.name)") - .accessibilityLabel("Delete \(macro.name.isEmpty ? "Unnamed" : macro.name)") + .help("Delete \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") + .accessibilityLabel("Delete \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") } } .padding(.horizontal, 12) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift index 11c8dd28..f0aa5c3f 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift @@ -222,16 +222,16 @@ struct ScriptRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit \(script.name.isEmpty ? "Untitled Script" : script.name)") - .accessibilityLabel("Edit \(script.name.isEmpty ? "Untitled Script" : script.name)") + .help("Edit \(script.name.isEmpty ? \"Untitled Script\" : script.name)") + .accessibilityLabel("Edit \(script.name.isEmpty ? \"Untitled Script\" : script.name)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete \(script.name.isEmpty ? "Untitled Script" : script.name)") - .accessibilityLabel("Delete \(script.name.isEmpty ? "Untitled Script" : script.name)") + .help("Delete \(script.name.isEmpty ? \"Untitled Script\" : script.name)") + .accessibilityLabel("Delete \(script.name.isEmpty ? \"Untitled Script\" : script.name)") } } .padding(.horizontal, 12) From 7006a9eec47f7ae4c2da89d09576c620fe59afde Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:35:21 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Dynamic=20accessi?= =?UTF-8?q?bility=20labels=20in=20lists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .../Views/Macros/MacroListView.swift | 12 ++++++------ .../Views/Scripts/ScriptListView.swift | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift index 87668c58..6844eec6 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift @@ -155,8 +155,8 @@ struct SharedMacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name) in shared library") - .accessibilityLabel("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name) in shared library") + .help("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name) + " in shared library") + .accessibilityLabel("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name) + " in shared library") } .padding(.horizontal, 12) .padding(.vertical, 8) @@ -206,16 +206,16 @@ struct MacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") - .accessibilityLabel("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") + .help("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name)) + .accessibilityLabel("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name)) Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") - .accessibilityLabel("Delete \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") + .help("Delete " + (macro.name.isEmpty ? "Unnamed" : macro.name)) + .accessibilityLabel("Delete " + (macro.name.isEmpty ? "Unnamed" : macro.name)) } } .padding(.horizontal, 12) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift index f0aa5c3f..c5f82862 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift @@ -222,16 +222,16 @@ struct ScriptRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit \(script.name.isEmpty ? \"Untitled Script\" : script.name)") - .accessibilityLabel("Edit \(script.name.isEmpty ? \"Untitled Script\" : script.name)") + .help("Edit " + (script.name.isEmpty ? "Untitled Script" : script.name)) + .accessibilityLabel("Edit " + (script.name.isEmpty ? "Untitled Script" : script.name)) Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete \(script.name.isEmpty ? \"Untitled Script\" : script.name)") - .accessibilityLabel("Delete \(script.name.isEmpty ? \"Untitled Script\" : script.name)") + .help("Delete " + (script.name.isEmpty ? "Untitled Script" : script.name)) + .accessibilityLabel("Delete " + (script.name.isEmpty ? "Untitled Script" : script.name)) } } .padding(.horizontal, 12) From a3ff79e2bcdb33d8dd5d37d5d0990f7fbf120102 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 09:50:20 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Dynamic=20accessi?= =?UTF-8?q?bility=20labels=20in=20lists?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com> --- .../Views/Macros/MacroListView.swift | 12 ++++++------ .../Views/Scripts/ScriptListView.swift | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift index 6844eec6..87668c58 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift @@ -155,8 +155,8 @@ struct SharedMacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name) + " in shared library") - .accessibilityLabel("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name) + " in shared library") + .help("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name) in shared library") + .accessibilityLabel("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name) in shared library") } .padding(.horizontal, 12) .padding(.vertical, 8) @@ -206,16 +206,16 @@ struct MacroRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name)) - .accessibilityLabel("Edit " + (macro.name.isEmpty ? "Unnamed" : macro.name)) + .help("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") + .accessibilityLabel("Edit \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete " + (macro.name.isEmpty ? "Unnamed" : macro.name)) - .accessibilityLabel("Delete " + (macro.name.isEmpty ? "Unnamed" : macro.name)) + .help("Delete \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") + .accessibilityLabel("Delete \(macro.name.isEmpty ? \"Unnamed\" : macro.name)") } } .padding(.horizontal, 12) diff --git a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift index c5f82862..f0aa5c3f 100644 --- a/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift +++ b/XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift @@ -222,16 +222,16 @@ struct ScriptRow: View { .foregroundColor(.accentColor) } .buttonStyle(.borderless) - .help("Edit " + (script.name.isEmpty ? "Untitled Script" : script.name)) - .accessibilityLabel("Edit " + (script.name.isEmpty ? "Untitled Script" : script.name)) + .help("Edit \(script.name.isEmpty ? \"Untitled Script\" : script.name)") + .accessibilityLabel("Edit \(script.name.isEmpty ? \"Untitled Script\" : script.name)") Button(action: onDelete) { Image(systemName: "trash") .foregroundColor(.red.opacity(0.8)) } .buttonStyle(.borderless) - .help("Delete " + (script.name.isEmpty ? "Untitled Script" : script.name)) - .accessibilityLabel("Delete " + (script.name.isEmpty ? "Untitled Script" : script.name)) + .help("Delete \(script.name.isEmpty ? \"Untitled Script\" : script.name)") + .accessibilityLabel("Delete \(script.name.isEmpty ? \"Untitled Script\" : script.name)") } } .padding(.horizontal, 12)