Skip to content

[Mac] Fix VoiceOver does not announce initial ComboBox state in Task and Project Details - #38595

Merged
kubaflo merged 3 commits into
dotnet:mainfrom
praveenkumarkarunanithi:fix-36471
Sep 16, 2026
Merged

kubaflo merged 3 commits into
dotnet:mainfrom
praveenkumarkarunanithi:fix-36471

Conversation

@praveenkumarkarunanithi

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Issue Details

VoiceOver on macOS does not announce the ComboBox control type or its initial "Collapsed" state when initially navigating to the Project and Category pickers in the .NET MAUI mobile project template (DeveloperBalance).
Unknown
On initial linear navigation (Control + Option + Right Arrow), VoiceOver reads only the control's descriptive label without indicating that it is an expandable ComboBox or that its current state is collapsed.

Root Cause

In earlier versions, static semantic properties (SemanticProperties.Description and SemanticProperties.Hint="State Collapsed") were assigned to the parent SfTextInputLayout container. The regression was introduced in PR #32286 , which attempted to make state announcements dynamic by removing the static "State Collapsed" hint and relying exclusively on SemanticScreenReader.Announce triggered via Picker.IsOpen property changes.
Because synthetic announcements only fire on value changes, no state announcement was produced when the control initially received focus.

Description of Change

Appended State Collapsed directly to the SemanticProperties.Hint of each native Picker on both the Project Details and Task Details pages.
Kept SfTextInputLayout responsible solely for visual presentation and floating labels.
Preserved existing bindings, commands, and dynamic expansion announcements from PR #32286 while ensuring the initial resting state is exposed to platform accessibility APIs.

Why Tests Were Not Added

This issue requires manual accessibility validation using platform screen readers (macOS VoiceOver and Android TalkBack) to verify focus traversal and spoken announcements. Because these accessibility announcements cannot be reliably validated via automated UI/unit tests, manual verification was performed across macOS, Android, and Windows.

Issues Fixed

Fixes #36471

Screenshots

Before Issue Fix After Issue Fix
BeforeFix.mov
AfterFix.mov

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 38595

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 38595"

@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Sep 16, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@praveenkumarkarunanithi praveenkumarkarunanithi added area-templates Project templates, Item Templates for Blazor and MAUI t/a11y Relates to accessibility a11y/sf-template-app labels Sep 16, 2026
@praveenkumarkarunanithi praveenkumarkarunanithi added the community ✨ Community Contribution label Sep 16, 2026
@sheiksyedm
sheiksyedm marked this pull request as ready for review September 16, 2026 13:36
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Sep 16, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 1 findings

See inline comments for details.

SelectedIndex="{Binding CategoryIndex}"
IsOpen="{Binding IsCategoryPickerExpanded, Mode=TwoWay}"
SemanticProperties.Hint="ComboBox to select a category. Activate to browse and select a category"/>
SemanticProperties.Hint="ComboBox to select a category. Activate to browse and select a category. State Collapsed"/>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🔍 AI-Generated Review (multi-model)

[major] Accessibility — This hard-codes State Collapsed into SemanticProperties.Hint even when IsOpen is true. Concrete scenario: open the picker, then return focus to it while it remains expanded; assistive tech can read a stale collapsed state from the semantic node. The same stale-state pattern is also introduced in TaskDetailPage.xaml:47.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI Review Summary

@praveenkumarkarunanithi — new AI review results are available based on commit 6156b76.

Gate No Tests Confidence Low Platform Mac Catalyst


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ⚠️ SKIPPED

No tests were detected in this PR.

Recommendation: Add tests to verify the fix using the write-tests-agent.


📋 Pre-Flight — Context & Validation

Issue: #36471 - Voice over is not announcing role as combo box and state as collapsed for "Project" combobox:A11y_.NET maui_user can creat a tak_Screen reader
PR: #38595 - [Mac] Fix VoiceOver does not announce initial ComboBox state in Task and Project Details
Platforms Affected: Mac Catalyst (primary); the template is cross-platform and the author also reports manual Android and Windows validation
Files Changed: 2 implementation, 0 test

Key Findings

  • The reported failure is the initial VoiceOver focus announcement for the Project and Category pickers: the role and collapsed state are not spoken before Picker.IsOpen changes.
  • The PR appends State Collapsed to each picker's static SemanticProperties.Hint; existing IsOpen bindings and page-model announcements continue to announce later expanded/collapsed transitions.
  • The gate was already skipped because the PR contains no detected tests. The PR says this behavior was validated manually because spoken screen-reader output is not reliably covered by existing automation.
  • Issue discussion contains conflicting reproduction evidence: one tester could not reproduce on two Macs, and current-source trusted-tester verification remains pending.
  • There are no inline review comments, submitted reviews, or technical PR discussion to incorporate.

Code Review Summary

Verdict: SKIPPED
Confidence: N/A
Errors: 0 | Warnings: 0 | Suggestions: 0

Key code review findings:

  • Dedicated expert review is intentionally deferred to STEP 5b by the execution contract; this pre-flight is context-only.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #38595 Append a static collapsed-state phrase to both native Picker hints while retaining dynamic IsOpen announcements ⚠️ SKIPPED (no tests detected) ProjectDetailPage.xaml, TaskDetailPage.xaml Original PR; manual accessibility validation claimed

🔬 Code Review — Deep Analysis

Code Review — PR #38595

Independent Assessment

What this changes:
Adds . State Collapsed to static SemanticProperties.Hint strings on two Picker controls:

  • src/Templates/src/templates/maui-mobile/Pages/ProjectDetailPage.xaml
  • src/Templates/src/templates/maui-mobile/Pages/TaskDetailPage.xaml

Both pickers already bind IsOpen two-way and both corresponding page models (ProjectDetailPageModel, TaskDetailPageModel) already announce expanded/collapsed transitions via SemanticScreenReader.Announce(...).

Inferred motivation:
Provide initial focus-time spoken state for VoiceOver (collapsed) before any IsOpen transition occurs.

Reconciliation with PR Narrative

Author claims:
PR restores initial ComboBox state announcement on Mac VoiceOver by appending static collapsed text while preserving dynamic expanded/collapsed announcements.

Agreement/disagreement:
Agree on the initial-announcement gap and intent.
Disagree that the current implementation is fully correct: hard-coding State Collapsed in static semantics can become stale while the picker is expanded, conflicting with actual current state.

Prior Review Reconciliation

No prior ❌ Error findings found across:

  • top-level PR reviews
  • inline PR review comments
  • PR issue comments

Blast Radius Assessment

  • Runs for all instances: Yes, for these two template pickers.
  • Startup impact: No direct startup-path change.
  • Static/shared state: No.

CI Status

  • Required-check result: undetermined (gh pr checks --required unavailable in this environment due unauthenticated GH CLI).
  • Classification: undetermined CI coverage.
  • Action taken: confidence capped to low.
  • Additional evidence: supplied gate status is Gate SKIPPED — no tests detected.

Findings

❌ Error — Static semantic state can be incorrect while picker is open

src/Templates/src/templates/maui-mobile/Pages/ProjectDetailPage.xaml:45 (same issue pattern in TaskDetailPage.xaml:47):

The new static hint always says State Collapsed, but picker state is mutable via IsOpen.
Concrete scenario: user opens picker (expanded), then focus returns to picker while still open; assistive tech can read stale collapsed state from the semantic node. Dynamic SemanticScreenReader.Announce calls are event-based and do not keep the node's static hint synchronized.

Failure-Mode Probing

  • Pages that do not use these pickers: no impact.
  • Expanded-state revisit: stale State Collapsed hint can contradict runtime state.
  • Handler reconnect/null lifecycle: not introduced by this XAML-only diff.

External Output Contract

Not applicable.

Verdict: NEEDS_CHANGES

Confidence: low

Summary:
The change improves initial announcement behavior but introduces a correctness issue in ongoing accessibility state representation. Because the semantic hint now embeds a mutable state as static text, it can become incorrect during normal interaction. With CI/test evidence undetermined (and gate skipped), the fix should be revised before merge.


🛠️ Try-Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 gpt-5.3-codex try-fix Runtime semantic-state metadata bound to Picker.IsOpen, rather than a fixed collapsed-state hint ⚠️ BLOCKED None Baseline establishment rejected the pre-existing dirty workspace, so the candidate could not be implemented. The permitted template build also failed with NETSDK1004 because project.assets.json was absent.
2 gpt-5.6-sol try-fix Remove authored Picker hints and rely on native Mac Catalyst accessibility to expose role and state ⚠️ BLOCKED None Baseline establishment again produced no safe edit allow-list. XML parsing passed on unchanged files but did not validate VoiceOver behavior.
PR PR #38595 Append State Collapsed to both static Picker hints while retaining dynamic IsOpen announcements ⚠️ SKIPPED (Gate) 2 XAML files No tests detected; author reports manual accessibility validation.

Attempt Details

Candidate 1 — Runtime semantic-state binding

The candidate planned to derive accessibility metadata from each Picker's live IsOpen state. Unlike the PR's fixed prose, the semantic state would remain synchronized after initial focus and later expansion/collapse transitions.

Result: Blocked before implementation. EstablishBrokenBaseline.ps1 refused to create .github/.baseline-state.json because the workspace already contained broad unrelated changes; without a RevertedFiles allow-list, the try-fix contract prohibited edits. The exact permitted command, dotnet build src/Templates/src/Microsoft.Maui.Templates.csproj --no-restore, ran once and failed with NETSDK1004 because artifacts/obj/Microsoft.Maui.Templates/project.assets.json did not exist.

Diff: None.

Failure analysis: This result does not validate or invalidate the runtime-state approach. It establishes only that candidate implementation was unsafe in the current baseline and that compilation could not proceed without an unrequested restore.

Candidate 2 — Native Picker semantics

The candidate planned to remove authored Picker accessibility hints and rely on the native Mac Catalyst Picker accessibility element to expose its role and collapsed/expanded state. This delegates semantic truth to the native control, unlike both the PR's fixed state phrase and candidate 1's XAML state binding.

Result: Blocked before implementation. EstablishBrokenBaseline.ps1 again rejected the pre-existing dirty workspace and created no RevertedFiles allow-list, so no production edit was permitted. The exact allowed command parsed both unchanged XAML files successfully.

Diff: None.

Failure analysis: The successful XML parse establishes only that the unchanged PR files are well-formed. It provides no evidence that native Mac Catalyst semantics expose the missing role/state, so this approach remains unvalidated.

Cross-Pollination

Skipped by execution contract.

Exhausted: Yes — both mandated models were invoked once; no cross-pollination or follow-up review was run.
Selected Fix: PR's fix provisionally — neither alternative could be implemented or behaviorally tested, and the Gate supplied no automated validation. STEP 5b should treat the selection as low-confidence and rely on its dedicated expert review plus manual VoiceOver evidence.


🏁 Report — Final Recommendation

⚠️ Final Recommendation: REQUEST CHANGES

Winner: pr-plus-reviewer

pr-plus-reviewer is the only implemented candidate that addresses both halves of the accessibility contract: an initial collapsed state and semantic metadata that remains accurate after Picker.IsOpen changes. The submitted PR improves initial VoiceOver output but hard-codes State Collapsed; the expert review confirmed that this can contradict the live expanded state when focus returns while the picker remains open.

Comparative analysis

Rank Candidate Implementation Validation evidence Assessment
1 pr-plus-reviewer Binds each picker hint to a computed state-aware page-model property and raises dependent-property notifications from IsOpen; preserves existing transition announcements diff --check passed; both XAML files parsed; template build was blocked by missing restore assets; no Mac Catalyst VoiceOver run Winner. Resolves the expert finding with a focused four-file change. Compilation and spoken behavior remain uncertain.
2 pr Appends fixed State Collapsed text to both picker hints Gate skipped because no tests were detected; author reports manual platform validation Improves first-focus output, but leaves the semantic node stale while expanded. Expert verdict: NEEDS_CHANGES.
3 try-fix-1 Proposed live semantic-state metadata bound to Picker.IsOpen Blocked before edits; template build failed with missing project.assets.json Sound direction and conceptually aligned with the winner, but there is no candidate diff to evaluate or adopt.
4 try-fix-2 Proposed removing authored picker hints and relying on native semantics Blocked before edits; only unchanged PR XAML parsed No implementation or VoiceOver evidence. It also risks losing the explicit descriptive hint that currently compensates for the reported native announcement gap.

No candidate passed or failed a regression test: the trusted Gate was skipped, both STEP 5a attempts were blocked before implementation, and pr-plus-reviewer could only complete structural XAML validation. The ordering therefore rests on demonstrated code correctness rather than regression-test success; no failed-regression candidate was ranked above a passing candidate.

Required PR change

Adopt pr-plus-reviewer/reviewer.patch. It replaces the fixed hints with CategoryPickerSemanticHint and ProjectPickerSemanticHint, each derived from the corresponding observable IsOpen property and kept synchronized through NotifyPropertyChangedFor.

Remaining validation gap

The targeted template build ran exactly once and was blocked by NETSDK1004 because restore assets were absent. Both modified XAML files are well formed, but the winning patch still needs a restored template/sample build and manual Mac Catalyst VoiceOver verification before merge.


🧭 Next Steps — reviewer changes required

The reviewer-enhanced candidate identified changes that are not yet in the submitted PR.

Why: The reviewer-enhanced PR is the only implemented candidate that supplies the initial collapsed announcement without leaving static semantic metadata stale while the picker is expanded. Structural XAML validation passed, but compilation and Mac Catalyst VoiceOver behavior remain unverified because the targeted build was blocked by missing restore assets.

Address the actionable findings in this review before merging.

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) and removed s/agent-review-in-progress AI review is currently running for this PR labels Sep 16, 2026
@kubaflo
kubaflo merged commit b0a532a into dotnet:main Sep 16, 2026
40 checks passed
@kubaflo

kubaflo commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

/backport to release/10.0.1xx-sr11

@github-actions

Copy link
Copy Markdown
Contributor

Started backporting to release/10.0.1xx-sr11 (link to workflow run)

This branch was successfully deployed

1 active deployment
copilot-pat-pool — 6156b76f Deployed Sep 16, 2026 by praveenkumarkarunanithi via conclusion #1925
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y/sf-template-app area-templates Project templates, Item Templates for Blazor and MAUI community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-win AI found a better alternative fix than the PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) t/a11y Relates to accessibility

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Voice over is not announcing role as combo box and state as collapsed for "Project" combobox:A11y_.NET maui_user can creat a tak_Screen reader

4 participants