Skip to content

fix(react-popover): prevent incorrect non-modal screen reader announcement#36383

Closed
PaulGMardling wants to merge 15 commits into
microsoft:masterfrom
PaulGMardling:fix/a11y-popover-default
Closed

fix(react-popover): prevent incorrect non-modal screen reader announcement#36383
PaulGMardling wants to merge 15 commits into
microsoft:masterfrom
PaulGMardling:fix/a11y-popover-default

Conversation

@PaulGMardling

@PaulGMardling PaulGMardling commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes the incorrect Narrator announcement for non-modal Popover when content is non-interactive.

Previously, activating this scenario could produce:
contains style group

What Changed

PopoverSurface no longer applies role="group" unconditionally when trapFocus is false.
role="group" is now applied only when an accessible name is provided via aria-label or aria-labelledby, aligning with WAI-ARIA 1.2 requirements.
If no accessible name is provided, the surface renders as a plain div, so Narrator no longer announces contains style group.
Aria attribute logic was extracted into a pure function named getPopoverSurfaceAriaAttributes for isolated unit testing.
The default Popover story was updated to include aria-label as the recommended usage pattern.
Added a dev-only warning for non-modal popovers that open with non-interactive, non-focusable content to guide consumers toward accessible patterns.

Previous Behavior

On activating a popover with no interactive elements, Narrator announced:
contains style group

New Behavior

Narrator no longer announces contains style group for a non-modal popover surface without an accessible name.

Notes

This change resolves the incorrect announcement and adds consumer guidance for the non-interactive-content edge case.

Work Item

Fixes #18661

@PaulGMardling PaulGMardling changed the title Fix/a11y popover default fix(react-popover): omit role='group' from PopoverSurface when no accessible name is provided Jul 10, 2026
@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown

📊 Bundle size report

Package & Exports Baseline (minified/GZIP) PR Change
react-charts
AreaChart
404.329 kB
126.469 kB
404.524 kB
126.546 kB
195 B
77 B
react-charts
DeclarativeChart
754.978 kB
221.556 kB
755.173 kB
221.627 kB
195 B
71 B
react-charts
DonutChart
315.1 kB
97.161 kB
315.295 kB
97.237 kB
195 B
76 B
react-charts
FunnelChart
306.341 kB
93.907 kB
306.536 kB
93.975 kB
195 B
68 B
react-charts
GanttChart
387.386 kB
120.871 kB
387.581 kB
120.939 kB
195 B
68 B
react-charts
GaugeChart
314.433 kB
96.548 kB
314.628 kB
96.63 kB
195 B
82 B
react-charts
GroupedVerticalBarChart
395.54 kB
123.695 kB
395.735 kB
123.762 kB
195 B
67 B
react-charts
HeatMapChart
389.494 kB
121.928 kB
389.689 kB
121.996 kB
195 B
68 B
react-charts
HorizontalBarChart
294.866 kB
89.744 kB
295.061 kB
89.826 kB
195 B
82 B
react-charts
LineChart
415.68 kB
129.602 kB
415.875 kB
129.673 kB
195 B
71 B
react-charts
PolarChart
343.353 kB
107.405 kB
343.548 kB
107.474 kB
195 B
69 B
react-charts
SankeyChart
212.023 kB
68.066 kB
212.218 kB
68.134 kB
195 B
68 B
react-charts
ScatterChart
395.006 kB
123.651 kB
395.201 kB
123.716 kB
195 B
65 B
react-charts
VerticalBarChart
431.938 kB
128.619 kB
432.133 kB
128.69 kB
195 B
71 B
react-charts
VerticalStackedBarChart
401.558 kB
125.102 kB
401.753 kB
125.161 kB
195 B
59 B
react-components
react-components: Accordion, Button, FluentProvider, Image, Menu, Popover
225.836 kB
67.998 kB
226.031 kB
68.064 kB
195 B
66 B
react-components
react-components: entire library
1.294 MB
326.017 kB
1.294 MB
326.083 kB
195 B
66 B
react-popover
Popover
125.312 kB
40.384 kB
125.507 kB
40.45 kB
195 B
66 B
react-teaching-popover
TeachingPopover
100.504 kB
31.752 kB
100.559 kB
31.764 kB
55 B
12 B
Unchanged fixtures
Package & Exports Size (minified/GZIP)
react-avatar
Avatar
47.22 kB
14.855 kB
react-avatar
AvatarGroup
16.254 kB
6.502 kB
react-avatar
AvatarGroupItem
60.339 kB
18.758 kB
react-charts
HorizontalBarChartWithAxis
63 B
83 B
react-charts
Legends
234.402 kB
70.454 kB
react-charts
Sparkline
80.888 kB
26.864 kB
react-components
react-components: Button, FluentProvider & webLightTheme
66.281 kB
19.002 kB
react-components
react-components: FluentProvider & webLightTheme
39.504 kB
13.112 kB
react-datepicker-compat
DatePicker Compat
216.034 kB
63.912 kB
react-dialog
Dialog (including children components)
89.951 kB
27.858 kB
react-headless-components-preview
react-headless-components-preview: entire library
247.531 kB
71.417 kB
react-persona
Persona
54.173 kB
16.791 kB
react-portal-compat
PortalCompatProvider
5.341 kB
2.146 kB
react-table
DataGrid
146.759 kB
43.576 kB
react-table
Table (Primitives only)
36.893 kB
12.305 kB
react-table
Table as DataGrid
118.503 kB
33.331 kB
react-table
Table (Selection only)
65.373 kB
18.591 kB
react-table
Table (Sort only)
64.036 kB
18.204 kB
react-tag-picker
@fluentui/react-tag-picker - package
173.82 kB
54.128 kB
react-tags
InteractionTag
12.463 kB
4.942 kB
react-tags
Tag
28.376 kB
8.9 kB
react-tags
TagGroup
69.648 kB
21.404 kB
react-timepicker-compat
TimePicker
140.677 kB
45.99 kB
react-tree
FlatTree
135.571 kB
40.413 kB
react-tree
PersonaFlatTree
137.417 kB
40.931 kB
react-tree
PersonaTree
133.498 kB
39.718 kB
react-tree
Tree
131.658 kB
39.221 kB
🤖 This report was generated against bdb7bfaf86a92bbdc5519708ba01361001fa5744

@github-actions

Copy link
Copy Markdown

Pull request demo site: URL

@@ -0,0 +1,7 @@
{

@github-actions github-actions Bot Jul 10, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🕵🏾‍♀️ visual changes to review in the Visual Change Report

vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.chromium.png 910 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 628 Changed
vr-tests-react-components/ProgressBar converged 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 40 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 34 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 2 Changed
vr-tests-react-components/TagPicker 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - Dark Mode.chromium.png 658 Changed
vr-tests-react-components/TagPicker.disabled.chromium.png 677 Changed

There were 2 duplicate changes discarded. Check the build logs for more information.

@PaulGMardling
PaulGMardling force-pushed the fix/a11y-popover-default branch from eaac4f0 to ad5e853 Compare July 14, 2026 10:15
@PaulGMardling PaulGMardling changed the title fix(react-popover): omit role='group' from PopoverSurface when no accessible name is provided fix(react-popover): prevent incorrect non-modal screen reader announcement Jul 14, 2026
@PaulGMardling
PaulGMardling force-pushed the fix/a11y-popover-default branch from 1a1be75 to ed33516 Compare July 14, 2026 13:43
@PaulGMardling
PaulGMardling marked this pull request as ready for review July 14, 2026 14:35
@PaulGMardling
PaulGMardling requested review from a team as code owners July 14, 2026 14:35
…faceAriaAttributes

- Remove isolated unit tests for getPopoverSurfaceAriaAttributes function
- Keep component-level integration tests that verify aria attributes through rendered component
- Update function comment to remove mention of isolated testing
- Component logic is now tested as a whole rather than in isolation
…shaking

- Change condition from 'process.env.NODE_ENV === production' to '!== production'
- Move development-only warning logic inside the production check
- Ensures entire dev warning useEffect is dropped in production builds
- Follows Fluent UI pattern for dev-only code with guaranteed tree-shaking
- Replace double negation operator (!!) with explicit Boolean() call
- Improves code readability and follows codebase conventions
- No functional change, same type coercion behavior
- Remove nonInteractiveContentWarning constant from public API (constants.ts)
- Inline warning message directly in console.warn() call in usePopover.ts
- Removes logging noise from public API exports
- Update tests to use inlined warning message instead of constant reference
@PaulGMardling
PaulGMardling force-pushed the fix/a11y-popover-default branch from 37dacef to b02f3bb Compare July 17, 2026 13:28
Comment thread change/@fluentui-react-popover-bd6da173-2885-437a-98d5-f1c9a6e312c1.json Outdated
@bsunderhus

Copy link
Copy Markdown
Contributor

I'm not sure I follow the intent of this changes to be honest. The usage of popover itself is explicitly for focusable content. this incorrect non-modal screen reader announcement is referring to a usage of a popover without focusable content, if you have non-focusable content, why are you even using popover?

image

Probably tooltip is more of what you're looking for?!

I don't see the point in modifying something that is even triggering changes on snapshots in our codebase for a use case that we do not actually support.

@PaulGMardling

Copy link
Copy Markdown
Contributor Author

I'm not sure I follow the intent of this changes to be honest. The usage of popover itself is explicitly for focusable content. this incorrect non-modal screen reader announcement is referring to a usage of a popover without focusable content, if you have non-focusable content, why are you even using popover?

image Probably tooltip is more of what you're looking for?!

I don't see the point in modifying something that is even triggering changes on snapshots in our codebase for a use case that we do not actually support.

@bsunderhus
I'm not sure I follow the intent of these changes to be honest. The usage of popover itself is explicitly for focusable content. This incorrect non-modal screen reader announcement is referring to a usage of a popover without focusable content; if you have non-focusable content, why are you even using popover?

Thanks, this is a good callout. I agree Popover is generally intended for focusable/interactive content, and Tooltip is often a better fit for purely informational content.

The intent of this PR is narrower: it fixes the ARIA semantics PopoverSurface emits for existing non-modal usage. Today we can render role="group" without an accessible name, which leads to a misleading announcement ("contains style group") and does not align with expected ARIA semantics for group.

So this change is not trying to endorse non-focusable Popover usage as best practice. It is making the current output less misleading and more correct for assistive tech when that usage exists.

The snapshot changes also highlight that we already have tooltip-like scenarios in Fluent UI v9 that currently flow through Popover. That is useful signal for follow-up cleanup, but separate from this correctness fix.

This PR addresses the immediate a11y semantics issue (unlabeled role="group" causing misleading announcements). A separate follow-up can audit those callout patterns and migrate clearly non-interactive cases to Tooltip semantics where appropriate.

I don't see the point in modifying something that is even triggering changes on snapshots in our codebase for a use case that we do not actually support.

Thanks, I hear you. I agree we should avoid churn for unsupported scenarios when possible.

In this case, the snapshot changes are a side effect of correcting accessibility semantics, not the reason for the change. We were emitting role="group" without an accessible name in non-modal surfaces, which produced misleading screen reader output ("contains style group"). Keeping that behavior because some usages are not ideal would mean preserving incorrect ARIA output in shipped code.

So the tradeoff here is intentional: fix the semantics now, accept the snapshot updates that reflect that correction, and handle broader consumer-pattern alignment (Tooltip vs Popover) as a separate follow-up.

Co-authored-by: Victor Genaev <vgenaev@gmail.com>
@PaulGMardling

PaulGMardling commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Closing as the ADO item #18661
Is fixed on a story level, without changing the internal components at all refer to PR: #36406

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants