From bbe6cc9b0e3defb5713de2959444d820108eba30 Mon Sep 17 00:00:00 2001 From: Ben Henning Date: Wed, 3 Dec 2025 15:10:15 -0800 Subject: [PATCH] fix: Improve ARIA region handling. (#9485) ## The basics - [x] I [validated my changes](https://developers.google.com/blockly/guides/contribute/core#making_and_verifying_a_change) ## The details ### Resolves Fixes #9451 ### Proposed Changes Removes the ARIA `region` role for both flyouts and workspaces that are within mutators. ### Reason for Changes The use of the `region` role only adds confusion and slightly messes up region announcements for screen readers. `generic` has been used instead since it's the default container role (e.g. for `div`) and seems sufficient for what needs to be described in this case. Note that the delayed initialization for the flyout role is due to flyout initialization happening a bit later than its workspace DOM creation (so it doesn't seem possible to check for mutator status yet). There might be ways of doing this a bit more cleanly as part of #9307. ### Test Coverage No automated tests are needed for this experimental change. Manual testing comprised of navigating between the main workspace, the main workspace's toolbox and flyout, and a mutator workspace and flyout to validate that no unusual region readouts were happening. The accessibility node tree was also analyzed to verify that `generic` is correctly being applied as the role for the mutator workspace and flyout. ### Documentation No new documentation is needed for this experimental change. ### Additional Information This doesn't fully resolve all region issues, but it resolves the main ones (especially when combined with #9483 for NVDA). The main remaining problem at this point is that the main workspace itself is usually not read out as a region and it's not clear why. I suspect it has something to do with focus manager and how it automatically moves focus, but I'm not entirely sure what specific mechanism is causing the problem since both toolbox and flyout do something similar and don't have the same issue (flyout is particularly noteworthy since it's a workspace in itself). There may be some other focus oddities happening to cause the difference but, for now, this seems reasonable. If testing or user feedback find that the lack of consistent region readout is problematic for the main workspace then a new issue can be opened and investigated separately. --- core/flyout_base.ts | 3 +++ core/utils/aria.ts | 1 + core/workspace_svg.ts | 7 ++++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/core/flyout_base.ts b/core/flyout_base.ts index 7caf98c9b..8d4264f46 100644 --- a/core/flyout_base.ts +++ b/core/flyout_base.ts @@ -338,6 +338,9 @@ export abstract class Flyout init(targetWorkspace: WorkspaceSvg) { this.targetWorkspace = targetWorkspace; this.workspace_.targetWorkspace = targetWorkspace; + if (this.targetWorkspace.isMutator) { + aria.setRole(this.workspace_.getFocusableElement(), aria.Role.GENERIC); + } this.workspace_.scrollbar = new ScrollbarPair( this.workspace_, diff --git a/core/utils/aria.ts b/core/utils/aria.ts index 84d4e2312..64d1bf143 100644 --- a/core/utils/aria.ts +++ b/core/utils/aria.ts @@ -54,6 +54,7 @@ export enum Role { COMBOBOX = 'combobox', SPINBUTTON = 'spinbutton', REGION = 'region', + GENERIC = 'generic', } /** diff --git a/core/workspace_svg.ts b/core/workspace_svg.ts index e09618bb9..b919fa5c7 100644 --- a/core/workspace_svg.ts +++ b/core/workspace_svg.ts @@ -762,14 +762,19 @@ export class WorkspaceSvg }); let ariaLabel = null; + let role: aria.Role | null = null; if (this.isFlyout) { ariaLabel = 'Flyout'; + // Default to region, but this may change during flyout initialization. + role = aria.Role.REGION; } else if (this.isMutator) { ariaLabel = 'Mutator Workspace'; + role = aria.Role.GENERIC; } else { ariaLabel = Msg['WORKSPACE_ARIA_LABEL']; + role = aria.Role.REGION; } - aria.setRole(this.svgGroup_, aria.Role.REGION); + aria.setRole(this.svgGroup_, role); aria.setState(this.svgGroup_, aria.State.LABEL, ariaLabel); // Note that a alone does not receive mouse events--it must have a