Skip to content

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

Open
PaulGMardling wants to merge 12 commits into
microsoft:masterfrom
PaulGMardling:fix/a11y-popover-default
Open

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

Conversation

@PaulGMardling

@PaulGMardling PaulGMardling commented Jul 10, 2026

Copy link
Copy Markdown

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:
https://dev.azure.com/microsoftdesign/fluent-ui/_workitems/edit/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.33 kB
126.47 kB
404.525 kB
126.548 kB
195 B
78 B
react-charts
DeclarativeChart
754.979 kB
221.561 kB
755.174 kB
221.632 kB
195 B
71 B
react-charts
DonutChart
315.101 kB
97.163 kB
315.296 kB
97.239 kB
195 B
76 B
react-charts
FunnelChart
306.342 kB
93.907 kB
306.537 kB
93.976 kB
195 B
69 B
react-charts
GanttChart
387.387 kB
120.871 kB
387.582 kB
120.939 kB
195 B
68 B
react-charts
GaugeChart
314.434 kB
96.549 kB
314.629 kB
96.632 kB
195 B
83 B
react-charts
GroupedVerticalBarChart
395.541 kB
123.696 kB
395.736 kB
123.766 kB
195 B
70 B
react-charts
HeatMapChart
389.495 kB
121.928 kB
389.69 kB
121.993 kB
195 B
65 B
react-charts
HorizontalBarChart
294.867 kB
89.75 kB
295.062 kB
89.832 kB
195 B
82 B
react-charts
LineChart
415.681 kB
129.6 kB
415.876 kB
129.671 kB
195 B
71 B
react-charts
PolarChart
343.354 kB
107.407 kB
343.549 kB
107.475 kB
195 B
68 B
react-charts
SankeyChart
212.023 kB
68.066 kB
212.218 kB
68.134 kB
195 B
68 B
react-charts
ScatterChart
395.007 kB
123.647 kB
395.202 kB
123.712 kB
195 B
65 B
react-charts
VerticalBarChart
431.939 kB
128.619 kB
432.134 kB
128.691 kB
195 B
72 B
react-charts
VerticalStackedBarChart
401.559 kB
125.101 kB
401.754 kB
125.159 kB
195 B
58 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.293 MB
325.78 kB
1.293 MB
325.872 kB
195 B
92 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.403 kB
70.457 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.532 kB
71.414 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 9c8dada6dc1d4ade2f59049748d93a3ba5802427

@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/Menu Converged - submenuIndicator slotted content 1 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Menu Converged - submenuIndicator slotted content.default - RTL.submenus open.chromium.png 404 Changed
vr-tests-react-components/Positioning 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/Positioning.Positioning end.chromium.png 510 Changed
vr-tests-react-components/Positioning.Positioning end.updated 2 times.chromium.png 618 Changed
vr-tests-react-components/ProgressBar converged 3 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - Dark Mode.default.chromium.png 83 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness - High Contrast.default.chromium.png 267 Changed
vr-tests-react-components/ProgressBar converged.Indeterminate + thickness.default.chromium.png 202 Changed
vr-tests-react-components/TagPicker 2 screenshots
Image Name Diff(in Pixels) Image Type
vr-tests-react-components/TagPicker.disabled - Dark Mode.disabled input hover.chromium.png 658 Changed
vr-tests-react-components/TagPicker.disabled - High Contrast.disabled input hover.chromium.png 1319 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
@PaulGMardling PaulGMardling force-pushed the fix/a11y-popover-default branch from ed33516 to a2e2d0e Compare July 14, 2026 14:55
…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
return;
}

// eslint-disable-next-line no-console

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Should we create a dev only es lint rule, or move this functionality to a separate dev-only utility?

@Hotell

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.

2 participants