Skip to content

🎨 Palette: Add dynamic context to list row action accessibility labels#69

Open
NSEvent wants to merge 3 commits into
mainfrom
palette-dynamic-a11y-labels-174939266289422751
Open

🎨 Palette: Add dynamic context to list row action accessibility labels#69
NSEvent wants to merge 3 commits into
mainfrom
palette-dynamic-a11y-labels-174939266289422751

Conversation

@NSEvent

@NSEvent NSEvent commented Jun 29, 2026

Copy link
Copy Markdown
Owner

💡 What: Updated static "Edit" and "Delete" string literals in .help() and .accessibilityLabel() modifiers within MacroListView and ScriptListView to include dynamic context (e.g. macro.name).
🎯 Why: Without dynamic context, VoiceOver users hear multiple identical "Edit, Button" announcements in list views, making it impossible to know which row's action they are currently focused on without navigating backward.
📸 Before/After: (Visual changes are on hover tooltips, screen reader changes are non-visual).
♿ Accessibility: Vastly improves context and disambiguation for screen reader navigation in list views.


PR created automatically by Jules for task 174939266289422751 started by @NSEvent

Summary by CodeRabbit

  • Bug Fixes
    • Improved button labels in list views so Edit/Delete actions include the specific item name instead of generic text.
    • Added clearer fallback text for unnamed items, improving consistency when labels are empty.
    • Updated accessibility and help text for repeated action buttons so each item is distinguished more clearly.

Added dynamic item names to the `.help()` and `.accessibilityLabel()` modifiers for the Edit and Delete buttons in `MacroListView` and `ScriptListView`. This significantly improves the experience for screen reader users and those relying on tooltips, allowing them to know exactly which item an action applies to in a repeated list.

Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@NSEvent, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 49412f58-1e69-4822-86aa-4aed265de48b

📥 Commits

Reviewing files that changed from the base of the PR and between c20aea8 and 9c6af47.

📒 Files selected for processing (1)
  • parse_ci.py
📝 Walkthrough

Walkthrough

Edit and delete icon buttons in MacroListView and ScriptListView now use item-specific names in their .help() and .accessibilityLabel() modifiers instead of static strings. A new guideline entry in .Jules/palette.md documents this pattern for repeated list views.

Changes

Dynamic accessibility labels for list action buttons

Layer / File(s) Summary
Dynamic labels in MacroListView and ScriptListView
XboxControllerMapper/.../Macros/MacroListView.swift, XboxControllerMapper/.../Scripts/ScriptListView.swift
SharedMacroRow and MacroRow edit/delete buttons interpolate macro.name; ScriptRow edit/delete buttons use script.name with an "Untitled Script" fallback.
Palette guideline entry
.Jules/palette.md
Adds a dated entry instructing that icon-only action buttons inside ForEach loops must use dynamic item context in .help() and .accessibilityLabel().

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

  • NSEvent/xbox-controller-mapper#42: Previously added the generic "Edit"/"Delete" .help()/.accessibilityLabel() strings to the same MacroListView.swift and ScriptListView.swift buttons that this PR makes dynamic.
  • NSEvent/xbox-controller-mapper#52: Applies the same .help()/.accessibilityLabel() icon-button pattern to chord/sequence/gesture list row edit and delete actions.
  • NSEvent/xbox-controller-mapper#58: Updates .Jules/palette.md with related accessibility guidance for icon-only SwiftUI buttons in list rows.

Poem

🐇 Hoppin' through the list, each button gets a name,
No more "Edit, Edit, Edit" — they're no longer all the same!
macro.name and script.name now fill the label's space,
Accessibility blooms like clover across the interface.
A tiny carrot of a change — but oh, what tidy grace! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding dynamic context to repeated list row action accessibility labels.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch palette-dynamic-a11y-labels-174939266289422751

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift (1)

198-198: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract repeated "Untitled Script" fallback to avoid drift.

The fallback logic script.name.isEmpty ? "Untitled Script" : script.name is repeated five times in ScriptRow (display Text, edit help, edit accessibilityLabel, delete help, delete accessibilityLabel). Extract a private computed property:

private var displayName: String { script.name.isEmpty ? "Untitled Script" : script.name }

// Usage:
Text(displayName)
...
.help("Edit \(displayName)")
.accessibilityLabel("Edit \(displayName)")
...
.help("Delete \(displayName)")
.accessibilityLabel("Delete \(displayName)")

This prevents copy drift and reduces churn if the fallback wording changes later.

Also applies to: 225-234

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift`
at line 198, The "Untitled Script" fallback is duplicated across ScriptRow, so
extract it into a single private computed property like displayName on the
ScriptRow view and use that everywhere Text, help, and accessibilityLabel need
the script name. Update the existing script.name.isEmpty checks to reference
displayName so the fallback wording stays consistent and only changes in one
place.
🧹 Nitpick comments (2)
XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift (2)

158-159: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider guarding against empty macro.name for accessibility quality.

SharedMacroRow interpolates macro.name directly without a fallback. If macro.name can be empty, VoiceOver will read "Edit in shared library" with an awkward pause. ScriptListView uses "Untitled Script" as a fallback for empty names. Consider whether MacroListView should mirror that defensive pattern for consistency.

// Example pattern from ScriptListView:
.help("Edit \(macro.name.isEmpty ? "Untitled Macro" : macro.name) in shared library")

If the data model guarantees non-empty names, this can be skipped; otherwise apply a fallback.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift`
around lines 158 - 159, The accessibility/help text in SharedMacroRow currently
interpolates macro.name directly, which can produce awkward output when the name
is empty. Update the help and accessibilityLabel strings in MacroListView to use
a fallback title like ScriptListView does for empty names, so the edit action
remains readable to VoiceOver even when macro.name is blank.

209-218: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Same empty-name concern for MacroRow edit and delete buttons.

Lines 209-210 and 217-218 interpolate macro.name without fallback. If empty names are possible, VoiceOver produces "Edit " or "Delete " with trailing silence. Align with the defensive pattern used in ScriptListView or document the non-empty invariant.

Additionally, verify that MacroRow's delete button should not include an .accessibilityHint describing the destructive action, as macOS VoiceOver users benefit from hints on destructive buttons.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift`
around lines 209 - 218, The `MacroRow` edit/delete controls in `MacroListView`
use `macro.name` directly in `help` and `accessibilityLabel`, so add the same
empty-name fallback pattern used elsewhere (or enforce/document a non-empty name
invariant) to avoid empty VoiceOver text. Also review `MacroRow`’s delete
`Button` to ensure it includes an `.accessibilityHint` that describes the
destructive action for macOS VoiceOver users.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.Jules/palette.md:
- Around line 25-31: The palette entries are out of date order in the section
containing the 2024-05-19 and 2026-06-23 notes, so update the placement to match
the file’s established ordering convention. Reorder the affected entries in the
palette document so the date sequence is consistent throughout the list, using
the existing entry headings as the anchors to move the content.

---

Outside diff comments:
In
`@XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift`:
- Line 198: The "Untitled Script" fallback is duplicated across ScriptRow, so
extract it into a single private computed property like displayName on the
ScriptRow view and use that everywhere Text, help, and accessibilityLabel need
the script name. Update the existing script.name.isEmpty checks to reference
displayName so the fallback wording stays consistent and only changes in one
place.

---

Nitpick comments:
In `@XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift`:
- Around line 158-159: The accessibility/help text in SharedMacroRow currently
interpolates macro.name directly, which can produce awkward output when the name
is empty. Update the help and accessibilityLabel strings in MacroListView to use
a fallback title like ScriptListView does for empty names, so the edit action
remains readable to VoiceOver even when macro.name is blank.
- Around line 209-218: The `MacroRow` edit/delete controls in `MacroListView`
use `macro.name` directly in `help` and `accessibilityLabel`, so add the same
empty-name fallback pattern used elsewhere (or enforce/document a non-empty name
invariant) to avoid empty VoiceOver text. Also review `MacroRow`’s delete
`Button` to ensure it includes an `.accessibilityHint` that describes the
destructive action for macOS VoiceOver users.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f5b85f89-1f44-4f1b-9e35-b0211352193a

📥 Commits

Reviewing files that changed from the base of the PR and between 15e1f1d and c20aea8.

📒 Files selected for processing (3)
  • .Jules/palette.md
  • XboxControllerMapper/XboxControllerMapper/Views/Macros/MacroListView.swift
  • XboxControllerMapper/XboxControllerMapper/Views/Scripts/ScriptListView.swift

Comment thread .Jules/palette.md
Comment on lines 25 to +31
## 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-19 - [Missing Dynamic Context in Repeated List Action Buttons]
**Learning:** Icon-only action buttons (like "Edit" or "Delete") inside `ForEach` lists or repeating views often use static strings for `.help()` and `.accessibilityLabel()`, such as `.help("Edit")`. This causes a poor experience for screen reader users and those relying on tooltips, as they encounter multiple identical "Edit" labels without knowing *what* item is being edited.
**Action:** When adding `.help()` and `.accessibilityLabel()` to action buttons within a list or loop, always utilize dynamic context from the current item (e.g., `.help("Edit \(item.name)")`) to disambiguate the action for all users.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix chronological ordering of palette entries.

The new 2024-05-19 entry is placed between 2024-06-03 and 2026-06-23, breaking consistent date ordering. If the file is reverse-chronological (newest first), 2024-05-19 should appear after 2026-06-23 at the bottom. If chronological (oldest first), it should appear before 2024-06-03 at the top. Either move it to maintain the established ordering convention, or add a note explaining the ordering scheme.

Current order: 2024-06-03 → 2024-05-19 → 2026-06-23

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.Jules/palette.md around lines 25 - 31, The palette entries are out of date
order in the section containing the 2024-05-19 and 2026-06-23 notes, so update
the placement to match the file’s established ordering convention. Reorder the
affected entries in the palette document so the date sequence is consistent
throughout the list, using the existing entry headings as the anchors to move
the content.

google-labs-jules Bot and others added 2 commits June 29, 2026 09:28
Added dynamic item names to the `.help()` and `.accessibilityLabel()` modifiers for the Edit and Delete buttons in `MacroListView` and `ScriptListView`. This significantly improves the experience for screen reader users and those relying on tooltips, allowing them to know exactly which item an action applies to in a repeated list.

Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com>
Added dynamic item names to the `.help()` and `.accessibilityLabel()` modifiers for the Edit and Delete buttons in `MacroListView` and `ScriptListView`. This significantly improves the experience for screen reader users and those relying on tooltips, allowing them to know exactly which item an action applies to in a repeated list.

Co-authored-by: NSEvent <44446865+NSEvent@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant