Skip to content

Hide unshown validation messages from VoiceOver - #105

Merged
dadachi merged 2 commits into
mainfrom
fix/hidden-validation-accessibility
Oct 15, 2026
Merged

dadachi merged 2 commits into
mainfrom
fix/hidden-validation-accessibility

Conversation

@dadachi

@dadachi dadachi commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

Problem

Forms keep their validation message in the layout and make it invisible with .foregroundStyle(... ? .validationError : .clear) while the field is valid. A clear Text is still an accessibility element, so VoiceOver reads an error that isn't there (e.g. "Name is required." under a filled-in name). The Android app had the same bug (found on a device 2026-10-16).

Fix

New validationMessage(isShown:) modifier (UI/Shared/ValidationMessage.swift): the validation color when shown, otherwise .clear plus .accessibilityHidden(true), so the layout stays stable. It is used at all 12 sites that hid a message this way:

  • SignUpView (name)
  • ShopkeeperEditView (name)
  • PasswordEditView (current password, confirm password / mismatch)
  • ShopCreateView (name, description)
  • ShopBasicSettingsView (name, description)
  • ItemTagCreateView (name, description)
  • ItemTagEditView (name, description)

The app target is not a synced group, so the new file is registered in project.pbxproj (4 lines, nothing else).

Test

ValidationMessageTest hosts VStack { Text(sibling); Text(message).validationMessage(isShown:) } in a UIHostingController in a window and walks the hosting view's accessibilityElements. SwiftUI builds those elements only while accessibility is on, so the test turns it on for the process with _AXSApplicationAccessibilitySetEnabled from the system's libAccessibility (via dlsym; no new dependency) and back off afterwards. The visible sibling label is asserted too, which proves the walk reaches SwiftUI's elements. Without enabling accessibility, the walk found no elements at all.

  • Removing .accessibilityHidden(!isShown) fails hiddenMessageIsNotAnAccessibilityElement (the bug reproduced).
  • .accessibilityHidden(true) fails shownMessageIsAnAccessibilityElement.
  • Full suite on a physical iPhone 17 Pro Max: 440 tests passed (1 known issue, already there). make lint is clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YDK5huvuRECyVrsQEdTN9q

dadachi and others added 2 commits October 16, 2026 04:51
Forms keep a validation message in the layout and make it invisible with
a clear foreground color while the field is valid. A clear Text is still
an accessibility element, so VoiceOver read errors that weren't there
(e.g. "Name is required." under a filled-in name).

Add validationMessage(isShown:), which applies the validation color or
clear and hides the text from accessibility while it isn't shown, and use
it at all 12 sites (sign-up, profile, password x2, shop create x2, shop
basic settings x2, item tag create x2, item tag edit x2).

ValidationMessageTest hosts the modifier in a UIHostingController with
app accessibility enabled and walks the accessibility elements: the
hidden message must be absent and the shown one present, with a visible
sibling Text proving the walk reaches SwiftUI's elements.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDK5huvuRECyVrsQEdTN9q
UIWindow(frame:) is deprecated in iOS 26 and warned in CI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YDK5huvuRECyVrsQEdTN9q
@dadachi
dadachi merged commit a980556 into main Oct 15, 2026
2 checks passed
@dadachi
dadachi deleted the fix/hidden-validation-accessibility branch October 15, 2026 20:44
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