Skip to content

Add visible focus ring for keyboard-focused semantics nodes#186540

Open
BilalRehman08 wants to merge 2 commits into
flutter:masterfrom
BilalRehman08:fix/web-semantics-focus-indicator
Open

Add visible focus ring for keyboard-focused semantics nodes#186540
BilalRehman08 wants to merge 2 commits into
flutter:masterfrom
BilalRehman08:fix/web-semantics-focus-indicator

Conversation

@BilalRehman08
Copy link
Copy Markdown

Fixes #186044

Problem

flt-semantics nodes with tabindex="0" receive keyboard focus but show no visible indicator, failing WCAG 2.4.7 (Focus Visible). A plain CSS outline rule on those elements does not work because the root flt-semantics node has filter: opacity(0%) applied — CSS filters composite the entire subtree, making any outline on descendant elements invisible too.

Solution

Added a singleton overlay div (.flt-focus-ring) appended as a direct child of flutter-view, outside the flt-semantics-host subtree and therefore unaffected by the opacity filter. On every DOM focus event, AccessibilityFocusManager positions this overlay over the focused element using getBoundingClientRect() and hides it again on blur or when the node stops being managed.

Files changed:

  • engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart — focus ring show/hide logic in AccessibilityFocusManager
  • engine/src/flutter/lib/web_ui/lib/src/engine/view_embedder/style_manager.dartfocusRingClass constant + CSS rule (position: fixed, pointer-events: none, outline: 3px solid Highlight, z-index: 9999)
  • engine/src/flutter/lib/web_ui/test/engine/view_embedder/style_manager_test.dart — test verifying focus ring CSS is applied

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide.
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making.
  • All existing and new tests are passing.

@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) platform-web Web applications specifically f: focus Focus traversal, gaining or losing focus labels May 14, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a visible focus ring for keyboard-navigated semantics nodes in the web engine to comply with WCAG 2.4.7. It adds logic to AccessibilityFocusManager to manage a focus ring element and defines its CSS styles in StyleManager. Feedback indicates that the focus ring implementation should be updated to support multi-view by making the ring element and its management methods instance-based rather than static. Additionally, the positioning logic needs to account for moving elements, and the visibility logic in the unmanage method should be conditional to avoid incorrectly hiding the ring.

Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart Outdated
Comment thread engine/src/flutter/lib/web_ui/lib/src/engine/semantics/focusable.dart Outdated
flt-semantics nodes live inside a filter:opacity(0%) subtree, so a plain
CSS outline on those elements is invisible. Added a singleton overlay div
(.flt-focus-ring) as a sibling of flt-semantics-host inside flutter-view,
outside the filtered subtree, repositioned via getBoundingClientRect() on
every DOM focus event and hidden on blur.

Fixes flutter#186044
- Make _focusRing an instance field instead of static so each view
  gets its own ring in multi-view setups
- Convert _ensureFocusRing to an instance method using _owner directly
- Guard _hideFocusRing in stopManaging to only hide when the element
  being unmanaged is actually the active element, preventing accidental
  hiding of a different view's focus ring during cleanup
@BilalRehman08 BilalRehman08 force-pushed the fix/web-semantics-focus-indicator branch from 5b738e6 to 98b73e6 Compare May 15, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) engine flutter/engine related. See also e: labels. f: focus Focus traversal, gaining or losing focus platform-web Web applications specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request][Web][A11y] Provide default or configurable visible focus indicator for focusable Flutter Web semantics nodes

1 participant