
0 opacity does not cause things to be marked invisible in the accessibility tree. Fix documentation. Bug: 822557 Change-Id: I5233bbea67fc73db50a27ab3536474ad66b2eb6f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3914226 Commit-Queue: Abigail Klein <abigailbklein@google.com> Reviewed-by: Abigail Klein <abigailbklein@google.com> Auto-Submit: Katie Dektar <katie@chromium.org> Cr-Commit-Position: refs/heads/main@{#1050741}
147 lines
6.2 KiB
Markdown
147 lines
6.2 KiB
Markdown
# Offscreen, Invisible and Size
|
|
|
|
This document explains how Chrome interprets the guidelines to apply the labels
|
|
Offscreen and Invisible to nodes, and how the bounding box is calculated.
|
|
|
|
## Background
|
|
|
|
In general, screen reading tools may be interested in all nodes regardless of
|
|
whether they are presented to sighted users, but other Accessibility tools may
|
|
care what is visible to sighted users.
|
|
|
|
Specifically, tools like Select-to-Speak and Switch Access should not look at
|
|
nodes which are “offscreen”, “invisible”, or size=(0,0), as these are not
|
|
visible on the screen for mouse interactions. On the other hand, ChromeVox and
|
|
other screen readers may care about some of those nodes, which allow developers
|
|
to insert buttons visible only to users with a screen reader, or to navigate
|
|
below the fold.
|
|
|
|
## Offscreen
|
|
In Chrome, we define Offscreen as:
|
|
|
|
>Any object is offscreen if it is fully clipped or scrolled out of view by any
|
|
of its ancestors so that it is not rendered on the screen.
|
|
|
|
For example, the staticText node here is offscreen:
|
|
```html
|
|
<div style="width:0; height; 0; overflow: hidden">
|
|
This text should be marked "offscreen", although its parent is not.
|
|
</div>
|
|
```
|
|
|
|
As background, [MSDN](https://msdn.microsoft.com/en-us/library/dd373609(VS.85).aspx)
|
|
defines Offscreen as an object is not rendered, but not because it was
|
|
programmatically hidden:
|
|
|
|
>The object is clipped or has scrolled out of view, but it is not
|
|
programmatically hidden. If the user makes the viewport larger, more of the
|
|
object will be visible on the computer screen.
|
|
|
|
In Chrome, we interpret this to mean that an object is fully clipped or
|
|
scrolled out of view by its parent or ancestors. The main difference is that
|
|
we are being explicit that any ancestor clipping a node can make it offscreen,
|
|
not just a rootWebArea or scrollable ancestor.
|
|
|
|
### Technical Implementation
|
|
Offscreen is calculated in [AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc).
|
|
In this function, we walk up the accessibility tree adjusting a node's bounding
|
|
box to the frame of its ancestors. If the box is clipped because it lies
|
|
outside an ancestor's bounds, and that ancestor clips its children (i.e.
|
|
overflow:hidden, overflow:scroll, or it is a rootWebArea), offscreen is set to
|
|
true.
|
|
|
|
## Invisible
|
|
A node is marked Invisible in Chrome if it is hidden programmatically. In some
|
|
cases, invisible nodes are simply excluded from the accessibility tree. Chrome
|
|
defines invisible as:
|
|
|
|
>Invisible means that a node or its ancestor is explicitly invisible.
|
|
|
|
This is the same as the definition from [MSDN](https://msdn.microsoft.com/en-us/library/dd373609(VS.85).aspx):
|
|
|
|
>The object is programmatically hidden.
|
|
|
|
For example, these nodes are invisible:
|
|
|
|
```html
|
|
<div style="display:none">
|
|
This text should be marked 'invisible', along with its parent div.
|
|
</div>
|
|
|
|
<div style="visibility:hidden">
|
|
This text should also be marked 'invisible' along with its parent div.
|
|
</div>
|
|
```
|
|
|
|
### Technical implementation
|
|
See `AXObject::IsVisible()` ([source](https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/accessibility/ax_object.cc)).
|
|
|
|
Note: `Opacity: 0` is explicitly not treated as invisible.
|
|
|
|
## Bounding box calculation
|
|
A node's bounding box (location and size) are calculated based on its
|
|
intrinsic width, height and location, and the sizes of its ancestors.
|
|
We calculate size clipped by ancestors by default, but can also expose an
|
|
unclipped size through the [automation API](https://developer.chrome.com/extensions/automation).
|
|
|
|
The unclipped bounding box is helpful if you want to know the current
|
|
coordinates of an element that's scrolled out of view, so you know how
|
|
much to scroll to bring it in view.
|
|
|
|
The clipped bounding box is helpful if you want to draw a visible bounding
|
|
box around the element on the screen. Clipping the bounding box helps
|
|
sighted users understand what container the element is in, even if it's
|
|
not currently visible. Without clipping you end up with elements floating
|
|
outside of windows.
|
|
|
|
### Technical implementation
|
|
A node's location and size are calculated in[AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc),
|
|
and so closely tied to the offscreen calculation. In this function, we walk up
|
|
the accessibility tree adjusting a node's bounding box to the frame of its
|
|
ancestors.
|
|
|
|
In general, this calculation is straight forward. But there are several edge
|
|
cases:
|
|
|
|
* If a node has no intrinsic size, its size will be taken from the union of
|
|
its children.
|
|
|
|
```html
|
|
<!-- The outer div here has no size, so we use its child for its bounding box -->
|
|
<div style="visibility:hidden" aria-hidden=false>
|
|
<div style="visibility:visible">
|
|
Visible text
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
* If a node still has no size after that union, its bounds will be set to the
|
|
size of the nearest ancestor which has size. However, this node will be marked
|
|
`offscreen`, because it isn't visible to the user.
|
|
|
|
* This is useful for exposing nodes to screen reader users.
|
|
|
|
In addition, [AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc)
|
|
is used to determine how location and size may be clipped by ancestors,
|
|
allowing bounding boxes to reflect the location of a node clipped to its
|
|
ancestors.
|
|
|
|
* If a node is fully clipped by its ancestors such that the intersection of its
|
|
bounds and an ancestor's bounds are size 0, it will be pushed to the nearest
|
|
edge of the ancestor with width 1 or height 1.
|
|
|
|
* We use width and height 1 instead of 0 to distinguish between empty or
|
|
unknown sized nodes vs known small or clipped sized nodes.
|
|
|
|
Both clipped and unclipped location and size are exposed through the
|
|
[Chrome automation API](https://developer.chrome.com/extensions/automation).
|
|
|
|
* `location` is the global location of a node as clipped by its ancestors. If
|
|
a node is fully scrolled off a rootWebArea in X, for example, its location will
|
|
be the height of the rootWebArea, and its height will be 1. The Y position and width will be unchanged.
|
|
|
|
* `unclippedLocation` is the global location of a node ignoring any clipping
|
|
by ancestors. If a node is fully scrolled off a rootWebArea in X, for example,
|
|
its location will simply be larger than the height of the rootWebArea, and its
|
|
size will also be unchanged.
|