a11y: VoiceOver support for Live Activity / Dynamic Island (T001)#19
Open
bruschill wants to merge 6 commits into
Open
a11y: VoiceOver support for Live Activity / Dynamic Island (T001)#19bruschill wants to merge 6 commits into
bruschill wants to merge 6 commits into
Conversation
📄 Docs staleness warningThis PR modifies user-facing Swift source files but does not update any page under Changed source files: What to check:
If this PR does not require a doc update (e.g., internal refactor, bug fix, test change), add the After updating |
Deep a11y audit fan-out (12 parallel agents + cross-validation) over Meshtastic/Views, Widgets/, and Meshtastic Watch App/Views found 20 confirmed finding clusters (5 Blocker, 11 High, 4 Medium) across ~60 locations. tasks.md is the persistent, resumable checklist — one checkbox per finding, grouped by severity, with file:line and the proposed fix pattern for each.
Three icon-only buttons (arrow.up.circle.fill send, x.circle.fill cancel-reply, face.smiling emoji picker) had no accessibilityLabel, so VoiceOver announced them as a bare "Button" with no indication of what they do. Fixed in both TextMessageField's legacy body (iOS <18/ macOS <15) and FormattingComposeArea (iOS 18+/macOS 15+) — six call sites total, one localized label per action, following the existing String(localized:) + comment convention from ChannelList.swift and MessageText.swift. Closes T002, T015, T016 in specs/016-accessibility-audit/tasks.md.
WidgetsLiveActivity.swift had zero accessibility calls despite being a Lock Screen / Dynamic Island Live Activity where the icon+number rows (person.2.fill glyph next to a bare fraction) and StatRow pairs read as meaningless separate elements, or nothing at all, under VoiceOver. Grouped each icon+count/fraction pair with .accessibilityElement(children: .ignore) and a combined, localized .accessibilityLabel: - Dynamic Island expanded trailing region (nodesOnline/totalNodes) - Dynamic Island compact-leading region (nodesOnline) - Dynamic Island minimal presentation (nodesOnline) - LiveActivityView header row (nodesOnline/totalNodes) Grouped StatRow (label + value) into one combined "label: value" element instead of two disconnected VoiceOver stops. TimerView (the countdown) now announces "Update in" as its label with the live-updating countdown text carried as accessibilityValue, so VoiceOver gets both the semantic purpose and the current countdown state instead of silently skipping the timer glyph and digits.
…closed by this PR
bruschill
force-pushed
the
a11y/live-activity
branch
from
July 23, 2026 03:56
e43b71f to
1ecf941
Compare
…c#2172 Adds the 7 missing keys for this branch's new String(localized:) calls (node-count announcements, StatRow label:value, countdown timer label, and the shared TextMessageField labels), matching the pattern every earlier a11y commit on this branch already established.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed?
Widgets/WidgetsLiveActivity.swifthad zero accessibility calls in the whole file — the Live Activity's Dynamic Island presentations and Lock Screen view were completely silent to VoiceOver. Added.accessibilityElement(children: .ignore)+ a localized.accessibilityLabelto the node-count text in four places:LiveActivityViewheader row — "X of Y nodes online"Also checks off T001 in
specs/016-accessibility-audit/tasks.md(no other tracker rows touched).Why did it change?
T001, Phase 1 "Blocker" severity in the accessibility audit (
specs/016-accessibility-audit/): a Live Activity with zero accessibility support is a hard blocker — VoiceOver users get no information at all from the Dynamic Island or Lock Screen presentation, both of which are meant to surface mesh status (nodes online) without opening the app.How is this tested?
swiftc -parseonWidgetsLiveActivity.swiftafter the edit (syntax-only).xcodebuild buildfor theMeshtasticscheme (Debug, iOS Simulator) — succeeded, including theWidgetsExtensiontarget.Manual Test Instructions
This one specifically needs verification in the widget extension target, not just the main app — accessibility modifiers on widget views can behave differently there.
Pass criteria: all 4 locations read as a single combined VoiceOver stop with the exact node-count phrase — no separate stops per glyph, and no generic "Text"/unlabeled announcement.
Checklist
.accessibilityLabeladditions with descriptivecomment:parameters already on each localized string.)docs/user/ordocs/developer/, and updated accordingly. No doc update needed — this is a presentational, VoiceOver-only accessibility addition with no visible UI or behavior change. Requesting theskip-docs-checklabel, consistent with prior presentational-only a11y PRs.