Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1c12368

Browse files
authored
[web] scale semantic text elements to match the desired focus ring size (#52586)
Due to https://g-issues.chromium.org/issues/40875151?pli=1&authuser=0 and a lack of an ARIA role for plain text nodes (after the removal of `role="text"` in WebKit recently), there is no way to customize the size of the screen reader focus ring for a plain text element. The focus ring always tightly hugs the text itself. A workaround implemented in this PR is to match the size of the text exactly to the desired focus ring size. This is done by measuring the size of the text in the DOM, then scaling it both vertically and horizontally to match the size requested by the framework for the corresponding semantics node. To avoid serious performance penalty, the following optimizations were included: - Nodes that are satisfiable by just an `aria-label` do not need this workaround, and are skipped. - Nodes that must use DOM text (e.g. links, buttons) but have ARIA roles that size them based on the element, do not need this workaround, and are skipped. - Nodes that do need the workaround are first measured in a single batch, incurring only one page reflow. Then they are all updated in a single batch without taking any further DOM measurements. This ensures that no matter how many text spans are rendered, only one reflow is needed to measure them all. - Nodes that need the workaround cache the previous label and size, and if they do not change, the size is not updated. Other changes: - Rename `LeafLabelRepresentation` to `LabelRepresentation`, because labels also apply to non-leaf nodes (e.g. `aria-label` may be applied to a container). - Rename `labelRepresentation` to `preferredLabelRepresentation`, because a particular label representation cannot be guaranteed. A node that currently looks like a leaf text node may turn out to be an empty container, and after adding children to it must switch from using DOM text to an `aria-label`. Therefore, role manager only specify a preference, but `LabelAndValue` ultimately decides which representation is usable. - Introduce `void initState()` in `PrimaryRoleManager` to be used for one-time initialization of state and DOM structure after all objects that are in a one-to-one relationship with each other create all the references needed to establish that relationship (`PrimaryRoleManager`, `SemanticsObject`, `element`, `owner`, etc). This is not available at the time the constructors are called. Fixes flutter/flutter#146774.
1 parent c674206 commit 1c12368

15 files changed

+851
-182
lines changed

lib/web_ui/lib/src/engine/dom.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,14 @@ extension DomElementExtension on DomElement {
589589
external JSNumber get _clientWidth;
590590
double get clientWidth => _clientWidth.toDartDouble;
591591

592+
@JS('offsetHeight')
593+
external JSNumber get _offsetHeight;
594+
double get offsetHeight => _offsetHeight.toDartDouble;
595+
596+
@JS('offsetWidth')
597+
external JSNumber get _offsetWidth;
598+
double get offsetWidth => _offsetWidth.toDartDouble;
599+
592600
@JS('id')
593601
external JSString get _id;
594602
String get id => _id.toDart;

lib/web_ui/lib/src/engine/semantics/checkable.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class Checkable extends PrimaryRoleManager {
5555
super.withBasics(
5656
PrimaryRole.checkable,
5757
semanticsObject,
58-
labelRepresentation: LeafLabelRepresentation.ariaLabel,
58+
preferredLabelRepresentation: LabelRepresentation.ariaLabel,
5959
) {
6060
addTappable();
6161
}

lib/web_ui/lib/src/engine/semantics/incrementable.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class Incrementable extends PrimaryRoleManager {
2828
// the one being focused on, but the internal `<input>` element.
2929
addLiveRegion();
3030
addRouteName();
31-
addLabelAndValue(labelRepresentation: LeafLabelRepresentation.ariaLabel);
31+
addLabelAndValue(preferredRepresentation: LabelRepresentation.ariaLabel);
3232

3333
append(_element);
3434
_element.type = 'range';

0 commit comments

Comments
 (0)