blob: 0740ac68f2dc5bd69f818030210860ce092978b2 [file] [log] [blame] [view]
Katie Dektarea84ef82017-12-04 17:17:331# Offscreen, Invisible and Size
2
3This document explains how Chrome interprets the guidelines to apply the labels
4Offscreen and Invisible to nodes, and how the bounding box is calculated.
5
6## Background
7
8In general, screen reading tools may be interested in all nodes regardless of
9whether they are presented to sighted users, but other Accessibility tools may
10care what is visible to sighted users.
11
12Specifically, tools like Select-to-Speak and Switch Access should not look at
13nodes which are offscreen”, invisible”, or size=(0,0), as these are not
14visible on the screen for mouse interactions. On the other hand, ChromeVox and
15other screen readers may care about some of those nodes, which allow developers
16to insert buttons visible only to users with a screen reader, or to navigate
17below the fold.
18
19## Offscreen
20In Chrome, we define Offscreen as:
21
22>Any object is offscreen if it is fully clipped or scrolled out of view by any
23of its ancestors so that it is not rendered on the screen.
24
25For example, the staticText node here is offscreen:
26```html
27<div style="width:0; height; 0; overflow: hidden">
28 This text should be marked "offscreen", although its parent is not.
29</div>
30```
31
32As background, [MSDN](https://msdn.microsoft.com/en-us/library/dd373609(VS.85).aspx)
33defines Offscreen as an object is not rendered, but not because it was
Quinten Yearsley317532d2021-10-20 17:10:3134programmatically hidden:
Katie Dektarea84ef82017-12-04 17:17:3335
36>The object is clipped or has scrolled out of view, but it is not
37programmatically hidden. If the user makes the viewport larger, more of the
38object will be visible on the computer screen.
39
40In Chrome, we interpret this to mean that an object is fully clipped or
41scrolled out of view by its parent or ancestors. The main difference is that
42we are being explicit that any ancestor clipping a node can make it offscreen,
43not just a rootWebArea or scrollable ancestor.
44
45### Technical Implementation
46Offscreen is calculated in [AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc).
47In this function, we walk up the accessibility tree adjusting a node's bounding
48box to the frame of its ancestors. If the box is clipped because it lies
49outside an ancestor's bounds, and that ancestor clips its children (i.e.
50overflow:hidden, overflow:scroll, or it is a rootWebArea), offscreen is set to
51true.
52
53## Invisible
Quinten Yearsley317532d2021-10-20 17:10:3154A node is marked Invisible in Chrome if it is hidden programmatically. In some
Katie Dektarea84ef82017-12-04 17:17:3355cases, invisible nodes are simply excluded from the accessibility tree. Chrome
56defines invisible as:
57
58>Invisible means that a node or its ancestor is explicitly invisible.
59
60This is the same as the definition from [MSDN](https://msdn.microsoft.com/en-us/library/dd373609(VS.85).aspx):
61
62>The object is programmatically hidden.
63
64For example, these nodes are invisible:
65
66```html
67<div style="display:none">
68 This text should be marked 'invisible', along with its parent div.
69</div>
70
71<div style="visibility:hidden">
72 This text should also be marked 'invisible' along with its parent div.
73</div>
Katie Dektarea84ef82017-12-04 17:17:3374```
75
Katie Dektar513c95c2022-09-23 18:36:3376### Technical implementation
77See `AXObject::IsVisible()` ([source](https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/accessibility/ax_object.cc)).
78
79Note: `Opacity: 0` is explicitly not treated as invisible.
80
Katie Dektarea84ef82017-12-04 17:17:3381## Bounding box calculation
82A node's bounding box (location and size) are calculated based on its
83intrinsic width, height and location, and the sizes of its ancestors.
84We calculate size clipped by ancestors by default, but can also expose an
85unclipped size through the [automation API](https://developer.chrome.com/extensions/automation).
86
87The unclipped bounding box is helpful if you want to know the current
88coordinates of an element that's scrolled out of view, so you know how
89much to scroll to bring it in view.
90
91The clipped bounding box is helpful if you want to draw a visible bounding
92box around the element on the screen. Clipping the bounding box helps
93sighted users understand what container the element is in, even if it's
94not currently visible. Without clipping you end up with elements floating
95outside of windows.
96
97### Technical implementation
98A node's location and size are calculated in[AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc),
99and so closely tied to the offscreen calculation. In this function, we walk up
100the accessibility tree adjusting a node's bounding box to the frame of its
101ancestors.
102
103In general, this calculation is straight forward. But there are several edge
104cases:
105
106* If a node has no intrinsic size, its size will be taken from the union of
107its children.
108
109```html
110 <!-- The outer div here has no size, so we use its child for its bounding box -->
111 <div style="visibility:hidden" aria-hidden=false>
112 <div style="visibility:visible">
113 Visible text
114 </div>
115 </div>
116```
117
118* If a node still has no size after that union, its bounds will be set to the
119size of the nearest ancestor which has size. However, this node will be marked
120`offscreen`, because it isn't visible to the user.
121
122 * This is useful for exposing nodes to screen reader users.
123
124In addition, [AXTree::RelativeToTreeBounds](https://cs.chromium.org/chromium/src/ui/accessibility/ax_tree.cc)
125is used to determine how location and size may be clipped by ancestors,
126allowing bounding boxes to reflect the location of a node clipped to its
127ancestors.
128
129* If a node is fully clipped by its ancestors such that the intersection of its
130bounds and an ancestor's bounds are size 0, it will be pushed to the nearest
131edge of the ancestor with width 1 or height 1.
132
133 * We use width and height 1 instead of 0 to distinguish between empty or
134 unknown sized nodes vs known small or clipped sized nodes.
135
136Both clipped and unclipped location and size are exposed through the
137[Chrome automation API](https://developer.chrome.com/extensions/automation).
138
139* `location` is the global location of a node as clipped by its ancestors. If
140a node is fully scrolled off a rootWebArea in X, for example, its location will
141be the height of the rootWebArea, and its height will be 1. The Y position and width will be unchanged.
142
143* `unclippedLocation` is the global location of a node ignoring any clipping
144by ancestors. If a node is fully scrolled off a rootWebArea in X, for example,
145its location will simply be larger than the height of the rootWebArea, and its
146size will also be unchanged.