fix(hig): name the controls that were silent to VoiceOver - #2969
Merged
Merged
Conversation
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.
An audit of the app's SwiftUI and AppKit surfaces against Apple's accessibility guidance. 34 controls fixed, every one read in context first.
The pattern behind most of it
.help()was being used as if it were a label. On macOS.help()sets the tooltip and the accessibility hint. VoiceOver announces the label first and reads hints only after a delay, if the user has not switched them off. So a button carrying a perfectly good localized string in.help()and nothing in.accessibilityLabel()announces its SF Symbol name, or nothing.23 icon-only controls were in exactly that state: Open in Window on five different cell and field editors, Remove filter, Close parameter panel, Add Row, Delete Selected, Refresh Now, Copy install command, Bulk actions, Previous/Next Match, Replace, Replace All, Done, and more. The strings already existed and were already localized, so the fix is additive and the tooltip is unchanged.
What was worse than a missing label
PanelTextField) had neither a label nor a tooltip. The linked-folder enable switch hasEmptyView()as its label plus.labelsHidden(), so nothing named which folder it toggles; it now takes the folder's name.onTapGesture, which no assistive technology can reach, and choosing a row is the picker's whole purpose. The rows now carry.isButtonand anaccessibilityAction.sortDescriptorPrototype, so all of them are click-sortable.NSTableView.sortDescriptorspaints the arrow but reaches no accessibility client, whichSortableHeaderViewalready knows and handles; the plan outline did not. It now callssetAccessibilitySortDirectionon the sorted column and clears the rest.Picker("", …)plus.labelsHidden(), so each filter row offered two anonymous popups.No new catalog keys:
"Filter column","Filter operator"and"Selected"were all already there.Six reported defects that were not defects
Worth recording, because the scan that found them will find them again:
FavoriteStarButtonis.accessibilityHidden(true)on purpose; the row combines its children and exposes the toggle throughFavoriteAccessibilityAction.Label { Text(…) } icon: { … }, which is already named by its title: the compare plan warning, the activity-log outcome and the copy-objects error.LinkedFavoriteRowVieweven folds the file's encoding into it, so the non-UTF-8 warning is spoken.Deliberately not done
The audit also counted 107 decorative icons that are not
.accessibilityHidden(true). I fixed none of them. Doing five of 107 is worse than doing zero: it reads as inconsistent, and each misplaced.accessibilityHidden(true)deletes the only name a control has. That needs one sweep with each site read, as its own change.Verification
swiftlint lint --strictover all 27 changed Swift files: 0 violations.NSOutlineViewthrough the coordinator and readaccessibilitySortDirection()back off the header cells.setAccessibilitySortDirectioncall makes both behavioural cases fail, so they are testing the fix and not the test.