Slide 1

Slide 1 text

Edd Sowden
 Senior Developer 
 Government Digital Service
 @edds

Slide 2

Slide 2 text

Accessibility and how to get the most from your screenreader ! What even is a table? A quick look at Accessibility APIs GDS @edds

Slide 3

Slide 3 text

* I’m from the Government GDS @edds

Slide 4

Slide 4 text

We’re a team at the heart of government building digital public services GDS @edds

Slide 5

Slide 5 text

* We started by building GOV.UK GDS @edds

Slide 6

Slide 6 text

* The best place to find government services and information GDS @edds

Slide 7

Slide 7 text

GDS @edds

Slide 8

Slide 8 text

GDS @edds

Slide 9

Slide 9 text

GDS @edds

Slide 10

Slide 10 text

GDS @edds

Slide 11

Slide 11 text

GDS @edds

Slide 12

Slide 12 text

GOV.UK should work for everyone GDS @edds

Slide 13

Slide 13 text

I wrote a blog post

Slide 14

Slide 14 text

dimensions = ga:browser,ga:hour,ga:dayOfWeek metrics = ga:users filters = ga:browser==Internet Explorer Which would get you something like: (view with your own data in the Query Explorer) Then using the TSV export button in the Query Explorer, found at the bottom of the page, I can import the results into a Google Sheets and produce a quick graph of the data. For another example if we had a page which used a query string to let users filter by a date field. You could get back all the different dates people filtered by: Browser Hour Day of Week Users Internet Explorer 00 0 5895 Internet Explorer 00 1 6814 Internet Explorer 00 2 8115 Internet Explorer 00 3 8000 Internet Explorer 00 4 8077 … … … … GDS @edds

Slide 15

Slide 15 text

GDS @edds

Slide 16

Slide 16 text

table { display: block; overflow-x: scroll; } GDS @edds

Slide 17

Slide 17 text

GDS @edds

Slide 18

Slide 18 text

What does this do to accessibility software? GDS @edds

Slide 19

Slide 19 text

Listen to your websites

Slide 20

Slide 20 text

How many of you use Apple products regularly? GDS @edds

Slide 21

Slide 21 text

GDS @edds How many of you test your websites in a screenreader?

Slide 22

Slide 22 text

Voiceover comes with OS X and iOS GDS @edds

Slide 23

Slide 23 text

The Voiceover Training is really good GDS @edds

Slide 24

Slide 24 text

Let’s listen to it before I made the change GDS @edds

Slide 25

Slide 25 text

GDS @edds

Slide 26

Slide 26 text

And once I had added the extra CSS… GDS @edds

Slide 27

Slide 27 text

GDS @edds

Slide 28

Slide 28 text

The CSS display property affects how accessibility tech interprets the element GDS @edds

Slide 29

Slide 29 text

GDS @edds

Slide 30

Slide 30 text

I had lunch

Slide 31

Slide 31 text

“Can’t you just set an aria role of table?” @jaffathecake GDS @edds

Slide 32

Slide 32 text

GDS @edds

Slide 33

Slide 33 text

Cell GDS @edds Minimum viable table:

Slide 34

Slide 34 text

GDS @edds

Slide 35

Slide 35 text

GDS @edds

Slide 36

Slide 36 text

GDS @edds chrome://flags/#enable-devtools-experiments

Slide 37

Slide 37 text

GDS @edds

Slide 38

Slide 38 text

GDS @edds

Slide 39

Slide 39 text

GDS @edds

Slide 40

Slide 40 text

GDS @edds

Slide 41

Slide 41 text

GDS @edds

Slide 42

Slide 42 text

So our minimum viable table isn’t a table? GDS @edds

Slide 43

Slide 43 text

Let’s have a quick recap on Accessibility APIs GDS @edds

Slide 44

Slide 44 text

GDS @edds

Slide 45

Slide 45 text

GDS @edds

Slide 46

Slide 46 text

Accessibility APIs! ! Windows 7: MSAA/IAccessible OSX: NSAccessibility iOS: UI Accessibility Android: Accessibility Framework GDS @edds

Slide 47

Slide 47 text

role name state children GDS @edds

Slide 48

Slide 48 text

GDS @edds HTML to Platform Accessibility APIs Implementation Guide W3C Working Draft 01 October 2013 This version: http://www.w3.org/TR/2013/WD-html-aapi-20131001/ Latest published version: http://www.w3.org/TR/html-aapi/ Latest editor's draft: http://rawgithub.com/w3c/html-api-map/master/index.html Previous version: http://www.w3.org/TR/2012/WD-html-aapi-20130901/ Editors: Steve Faulkner, The Paciello Group, [email protected] Cynthia Shelly, Microsoft, [email protected] Jason Kiss, New Zealand Government, [email protected] Alexander Surkov, Mozilla Foundation, [email protected] Copyright © 2011-2013 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved. W3C liability, trademark and document use rules apply. Select text and file a bug

Slide 49

Slide 49 text

GDS @edds svg table table HTML4 Yes HTML5 Yes WAI-ARIA none MSAA + UIA Express Role: ROLE_SYSTEM_TABLE Use MSAA or UIA guidance MSAA + IAccessible2 Role: ROLE_SYSTEM_TABLE Relations: IA2_RELATION_LABELLED_BY by child caption if any Interface: IAccessibleTable2 UIA Control Type and Other Features Control Type: Data Grid Control Pattern: Table AT-SPI Role: ROLE_TABLE Relations: RELATION_LABELLED_BY by child caption if any Interface: Table AX AXRole: AXTable AXSubrole: (nil) AXRoleDescription: table tbody td

Slide 50

Slide 50 text

Browsers use the DOM and CSS to generate an accessibility tree. This is then passed onto accessibility APIs GDS @edds

Slide 51

Slide 51 text

So what does it look like for a “real” table? GDS @edds

Slide 52

Slide 52 text

GDS @edds

Slide 53

Slide 53 text

What do other browsers think of our minimum viable table? GDS @edds

Slide 54

Slide 54 text

GDS @edds

Slide 55

Slide 55 text

GDS @edds

Slide 56

Slide 56 text

GDS @edds

Slide 57

Slide 57 text

GDS @edds

Slide 58

Slide 58 text

GDS @edds

Slide 59

Slide 59 text

GDS @edds

Slide 60

Slide 60 text

GDS @edds

Slide 61

Slide 61 text

GDS @edds

Slide 62

Slide 62 text

JAWS GDS @edds

Slide 63

Slide 63 text

JAWS advances to the next table with the ’t’ shortcut GDS @edds

Slide 64

Slide 64 text

GDS @edds

Slide 65

Slide 65 text

GDS @edds

Slide 66

Slide 66 text

GDS @edds Cell

Slide 67

Slide 67 text

Browsers have heuristics to protect us from table based layouts GDS @edds

Slide 68

Slide 68 text

…… …… GDS @edds

Slide 69

Slide 69 text

GDS @edds …… ……

Slide 70

Slide 70 text

…… …… [ … 18 more rows … ] GDS @edds

Slide 71

Slide 71 text

GDS @edds …… …… [ … 18 more rows … ]

Slide 72

Slide 72 text

Green GDS @edds

Slide 73

Slide 73 text

GDS @edds Green

Slide 74

Slide 74 text

…… …… GDS @edds

Slide 75

Slide 75 text

GDS @edds …… ……

Slide 76

Slide 76 text

table { display: block; } ! …… …… GDS @edds

Slide 77

Slide 77 text

GDS @edds table { display: block; } ! …… ……

Slide 78

Slide 78 text

td { border-bottom: 1px solid; } ! …… …… GDS @edds

Slide 79

Slide 79 text

GDS @edds td { border-bottom: 1px solid; } ! …… ……

Slide 80

Slide 80 text

td { background: grey; } ! …… …… GDS @edds

Slide 81

Slide 81 text

GDS @edds td { background: grey; } ! …… ……

Slide 82

Slide 82 text

table { border-collapse: collapse; } td { background: grey; } ! …… …… GDS @edds

Slide 83

Slide 83 text

GDS @edds table { border-collapse: collapse; } td { background: grey; } ! …… ……

Slide 84

Slide 84 text

Let’s have a quick look under the hood in Chrome GDS @edds

Slide 85

Slide 85 text

GDS @edds 102. bool AXTable::isDataTable() const 103. { 104. if (!m_layoutObject || !node()) 105. return false; 106. 107. // Do not consider it a data table if it has an ARIA role. 108. if (hasARIARole()) 109. return false; 110. 111. // When a section of the document is contentEditable, all tables should be 112. // treated as data tables, otherwise users may not be able to work with rich 113. // text editors that allow creating and editing tables. 114. if (node() && node()->hasEditableStyle()) 115. return true; 116. 117. // This employs a heuristic to determine if this table should appear. 118. // Only "data" tables should be exposed as tables. 119. // Unfortunately, there is no good way to determine the difference 120. // between a "layout" table and a "data" table. 121. 122. LayoutTable* table = toLayoutTable(m_layoutObject); 123. Node* tableNode = table->node(); 124. if (!isHTMLTableElement(tableNode)) 125. return false; 126. 127. // Do not consider it a data table if any of its descendants have an ARIA role. 128. HTMLTableElement* tableElement = toHTMLTableElement(tableNode); 129. if (elementHasAriaRole(tableElement->tHead())) 130. return false; 131. if (elementHasAriaRole(tableElement->tFoot())) 132. return false; 133. 134. RefPtrWillBeRawPtr bodies = tableElement->tBodies(); 135. for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { 136. Element* bodyElement = bodies->item(bodyIndex);

Slide 86

Slide 86 text

GDS @edds 102. bool AXTable::isDataTable() const 103. { 104. if (!m_layoutObject || !node()) 105. return false; 106. 107. // Do not consider it a data table if it has an ARIA role. 108. if (hasARIARole()) 109. return false; 110. 111. // When a section of the document is contentEditable, all tables should be 112. // treated as data tables, otherwise users may not be able to work with rich 113. // text editors that allow creating and editing tables. 114. if (node() && node()->hasEditableStyle()) 115. return true; 116. 117. // This employs a heuristic to determine if this table should appear. 118. // Only "data" tables should be exposed as tables. 119. // Unfortunately, there is no good way to determine the difference 120. // between a "layout" table and a "data" table. 121. 122. LayoutTable* table = toLayoutTable(m_layoutObject); 123. Node* tableNode = table->node(); 124. if (!isHTMLTableElement(tableNode)) 125. return false; 126. 127. // Do not consider it a data table if any of its descendants have an ARIA role. 128. HTMLTableElement* tableElement = toHTMLTableElement(tableNode); 129. if (elementHasAriaRole(tableElement->tHead())) 130. return false; 131. if (elementHasAriaRole(tableElement->tFoot())) 132. return false; 133. 134. RefPtrWillBeRawPtr bodies = tableElement->tBodies(); 135. for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { 136. Element* bodyElement = bodies->item(bodyIndex); 102. bool AXTable::isDataTable() const 103. { 104. if (!m_layoutObject || !node()) 105. return false; 106. 107. // Do not consider it a data table if it has an ARIA role. 108. if (hasARIARole()) 109. return false; 110. 111. // When a section of the document is contentEditable, all tables should be 112. // treated as data tables, otherwise users may not be able to work with rich 113. // text editors that allow creating and editing tables. 114. if (node() && node()->hasEditableStyle()) 115. return true; 116. 117. // This employs a heuristic to determine if this table should appear. 118. // Only "data" tables should be exposed as tables. 119. // Unfortunately, there is no good way to determine the difference 120. // between a "layout" table and a "data" table. 121. 122. LayoutTable* table = toLayoutTable(m_layoutObject); 123. Node* tableNode = table->node(); 124. if (!isHTMLTableElement(tableNode)) 125. return false; 126. 127. // Do not consider it a data table if any of its descendants have an ARIA role. 128. HTMLTableElement* tableElement = toHTMLTableElement(tableNode); 129. if (elementHasAriaRole(tableElement->tHead())) 130. return false; 131. if (elementHasAriaRole(tableElement->tFoot())) 132. return false; 133. 134. RefPtrWillBeRawPtr bodies = tableElement->tBodies(); 135. for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) { 136. Element* bodyElement = bodies->item(bodyIndex);

Slide 87

Slide 87 text

GDS @edds 167. // cells have borders, or use attributes like headers, abbr, scope or axis 168. table->recalcSectionsIfNeeded(); 169. LayoutTableSection* firstBody = table->firstBody(); 170. if (!firstBody) 171. return false; 172. 173. int numCols = firstBody->numColumns(); 174. int numRows = firstBody->numRows(); 175. 176. // If there's only one cell, it's not a good AXTable candidate. 177. if (numRows == 1 && numCols == 1) 178. return false; 179. 180. // If there are at least 20 rows, we'll call it a data table. 181. if (numRows >= 20) 182. return true; 183. 184. // Store the background color of the table to check against cell's background colors. 185. const ComputedStyle* tableStyle = table->style(); 186. if (!tableStyle) 187. return false; 188. Color tableBGColor = tableStyle->visitedDependentColor(CSSPropertyBackgroundColor); 189. 190. // check enough of the cells to find if the table matches our criteria 191. // Criteria: 192. // 1) must have at least one valid cell (and) 193. // 2) at least half of cells have borders (or) 194. // 3) at least half of cells have different bg colors than the table, and there is cell spacing 195. unsigned validCellCount = 0; 196. unsigned borderedCellCount = 0; 197. unsigned backgroundDifferenceCellCount = 0; 198. unsigned cellsWithTopBorder = 0; 199. unsigned cellsWithBottomBorder = 0; 200. unsigned cellsWithLeftBorder = 0; 201. unsigned cellsWithRightBorder = 0; 167. // cells have borders, or use attributes like headers, abbr, scope or axis 168. table->recalcSectionsIfNeeded(); 169. LayoutTableSection* firstBody = table->firstBody(); 170. if (!firstBody) 171. return false; 172. 173. int numCols = firstBody->numColumns(); 174. int numRows = firstBody->numRows(); 175. 176. // If there's only one cell, it's not a good AXTable candidate. 177. if (numRows == 1 && numCols == 1) 178. return false; 179. 180. // If there are at least 20 rows, we'll call it a data table. 181. if (numRows >= 20) 182. return true; 183. 184. // Store the background color of the table to check against cell's background colors. 185. const ComputedStyle* tableStyle = table->style(); 186. if (!tableStyle) 187. return false; 188. Color tableBGColor = tableStyle->visitedDependentColor(CSSPropertyBackgroundColor); 189. 190. // check enough of the cells to find if the table matches our criteria 191. // Criteria: 192. // 1) must have at least one valid cell (and) 193. // 2) at least half of cells have borders (or) 194. // 3) at least half of cells have different bg colors than the table, and there is cell spacing 195. unsigned validCellCount = 0; 196. unsigned borderedCellCount = 0; 197. unsigned backgroundDifferenceCellCount = 0; 198. unsigned cellsWithTopBorder = 0; 199. unsigned cellsWithBottomBorder = 0; 200. unsigned cellsWithLeftBorder = 0; 201. unsigned cellsWithRightBorder = 0;

Slide 88

Slide 88 text

GDS @edds 294. return true; 295. 296. // if there is less than two valid cells, it's not a data table 297. if (validCellCount <= 1) 298. return false; 299. 300. // half of the cells had borders, it's a data table 301. unsigned neededCellCount = validCellCount / 2; 302. if (borderedCellCount >= neededCellCount 303. || cellsWithTopBorder >= neededCellCount 304. || cellsWithBottomBorder >= neededCellCount 305. || cellsWithLeftBorder >= neededCellCount 306. || cellsWithRightBorder >= neededCellCount) 307. return true; 308. 309. // half had different background colors, it's a data table 310. if (backgroundDifferenceCellCount >= neededCellCount) 311. return true; 312. 313. // Check if there is an alternating row background color indicating a zebra striped style pattern. 314. if (alternatingRowColorCount > 2) { 315. Color firstColor = alternatingRowColors[0]; 316. for (int k = 1; k < alternatingRowColorCount; k++) { 317. // If an odd row was the same color as the first row, its not alternating. 318. if (k % 2 == 1 && alternatingRowColors[k] == firstColor) 319. return false; 320. // If an even row is not the same as the first row, its not alternating. 321. if (!(k % 2) && alternatingRowColors[k] != firstColor) 322. return false; 323. } 324. return true; 325. } 326. 327. return false; 328. } 294. return true; 295. 296. // if there is less than two valid cells, it's not a data table 297. if (validCellCount <= 1) 298. return false; 299. 300. // half of the cells had borders, it's a data table 301. unsigned neededCellCount = validCellCount / 2; 302. if (borderedCellCount >= neededCellCount 303. || cellsWithTopBorder >= neededCellCount 304. || cellsWithBottomBorder >= neededCellCount 305. || cellsWithLeftBorder >= neededCellCount 306. || cellsWithRightBorder >= neededCellCount) 307. return true; 308. 309. // half had different background colors, it's a data table 310. if (backgroundDifferenceCellCount >= neededCellCount) 311. return true; 312. 313. // Check if there is an alternating row background color indicating a zebra striped style pattern. 314. if (alternatingRowColorCount > 2) { 315. Color firstColor = alternatingRowColors[0]; 316. for (int k = 1; k < alternatingRowColorCount; k++) { 317. // If an odd row was the same color as the first row, its not alternating. 318. if (k % 2 == 1 && alternatingRowColors[k] == firstColor) 319. return false; 320. // If an even row is not the same as the first row, its not alternating. 321. if (!(k % 2) && alternatingRowColors[k] != firstColor) 322. return false; 323. } 324. return true; 325. } 326. 327. return false; 328. }

Slide 89

Slide 89 text

What about the massive fox in the room? GDS @edds

Slide 90

Slide 90 text

GDS @edds

Slide 91

Slide 91 text

Firefox seems to be different depending on which screen reader you’re using GDS @edds

Slide 92

Slide 92 text

GDS @edds 922 // performance problems only. Note, currently 'aAllowEmpty' flag is used for 923 // caption element only. On another hand we create accessible object for 924 // the first entry of caption element (see 925 // HTMLTableAccessible::CacheChildren). 926 return !!elements->Item(1); 927 } 928 929 bool 930 HTMLTableAccessible::IsProbablyLayoutTable() 931 { 932 // Implement a heuristic to determine if table is most likely used for layout 933 // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells 934 // at the beginning or end of a row/col, and especially when they occur at the edge of a table? 935 // XXX expose this info via object attributes to AT-SPI 936 937 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC 938 // This will allow release trunk builds to be used by testers to refine the algorithm 939 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release 940 #ifdef SHOW_LAYOUT_HEURISTIC 941 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ 942 { \ 943 mLayoutHeuristic = isLayout ? \ 944 NS_LITERAL_STRING("layout table: " heuristic) : \ 945 NS_LITERAL_STRING("data table: " heuristic); \ 946 return isLayout; \ 947 } 948 #else 949 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } 950 #endif 951 952 DocAccessible* docAccessible = Document(); 953 if (docAccessible) { 954 uint64_t docState = docAccessible->State(); 955 if (docState & states::EDITABLE) { // Need to see all elements while document is being edited 956 RETURN_LAYOUT_ANSWER(false, "In editable document"); 957 } 958 } 959 960 // Check to see if an ARIA role overrides the role from native markup, 961 // but for which we still expose table semantics (treegrid, for example). 962 if (Role() != roles::TABLE) 963 RETURN_LAYOUT_ANSWER(false, "Has role attribute"); 964 965 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { 966 // Role attribute is present, but overridden roles have already been dealt with. 967 // Only landmarks and other roles that don't override the role from native 968 // markup are left to deal with here. 969 RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");

Slide 93

Slide 93 text

GDS @edds 922 // performance problems only. Note, currently 'aAllowEmpty' flag is used for 923 // caption element only. On another hand we create accessible object for 924 // the first entry of caption element (see 925 // HTMLTableAccessible::CacheChildren). 926 return !!elements->Item(1); 927 } 928 929 bool 930 HTMLTableAccessible::IsProbablyLayoutTable() 931 { 932 // Implement a heuristic to determine if table is most likely used for layout 933 // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells 934 // at the beginning or end of a row/col, and especially when they occur at the edge of a table? 935 // XXX expose this info via object attributes to AT-SPI 936 937 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC 938 // This will allow release trunk builds to be used by testers to refine the algorithm 939 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release 940 #ifdef SHOW_LAYOUT_HEURISTIC 941 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ 942 { \ 943 mLayoutHeuristic = isLayout ? \ 944 NS_LITERAL_STRING("layout table: " heuristic) : \ 945 NS_LITERAL_STRING("data table: " heuristic); \ 946 return isLayout; \ 947 } 948 #else 949 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } 950 #endif 951 952 DocAccessible* docAccessible = Document(); 953 if (docAccessible) { 954 uint64_t docState = docAccessible->State(); 955 if (docState & states::EDITABLE) { // Need to see all elements while document is being edited 956 RETURN_LAYOUT_ANSWER(false, "In editable document"); 957 } 958 } 959 960 // Check to see if an ARIA role overrides the role from native markup, 961 // but for which we still expose table semantics (treegrid, for example). 962 if (Role() != roles::TABLE) 963 RETURN_LAYOUT_ANSWER(false, "Has role attribute"); 964 965 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { 966 // Role attribute is present, but overridden roles have already been dealt with. 967 // Only landmarks and other roles that don't override the role from native 968 // markup are left to deal with here. 969 RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table"); 922 // performance problems only. Note, currently 'aAllowEmpty' flag is used for 923 // caption element only. On another hand we create accessible object for 924 // the first entry of caption element (see 925 // HTMLTableAccessible::CacheChildren). 926 return !!elements->Item(1); 927 } 928 929 bool 930 HTMLTableAccessible::IsProbablyLayoutTable() 931 { 932 // Implement a heuristic to determine if table is most likely used for layout 933 // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells 934 // at the beginning or end of a row/col, and especially when they occur at the edge of a table? 935 // XXX expose this info via object attributes to AT-SPI 936 937 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC 938 // This will allow release trunk builds to be used by testers to refine the algorithm 939 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release 940 #ifdef SHOW_LAYOUT_HEURISTIC 941 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ 942 { \ 943 mLayoutHeuristic = isLayout ? \ 944 NS_LITERAL_STRING("layout table: " heuristic) : \ 945 NS_LITERAL_STRING("data table: " heuristic); \ 946 return isLayout; \ 947 } 948 #else 949 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } 950 #endif 951 952 DocAccessible* docAccessible = Document(); 953 if (docAccessible) { 954 uint64_t docState = docAccessible->State(); 955 if (docState & states::EDITABLE) { // Need to see all elements while document is being edited 956 RETURN_LAYOUT_ANSWER(false, "In editable document"); 957 } 958 } 959 960 // Check to see if an ARIA role overrides the role from native markup, 961 // but for which we still expose table semantics (treegrid, for example). 962 if (Role() != roles::TABLE) 963 RETURN_LAYOUT_ANSWER(false, "Has role attribute"); 964 965 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { 966 // Role attribute is present, but overridden roles have already been dealt with. 967 // Only landmarks and other roles that don't override the role from native 968 // markup are left to deal with here. 969 RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");

Slide 94

Slide 94 text

GDS @edds 1089 1090 if (childIdx > 0 && prevRowColor != rowColor) 1091 RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); 1092 } 1093 } 1094 1095 // Check for many rows 1096 const uint32_t kMaxLayoutRows = 20; 1097 if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099 } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 { 1089 1090 if (childIdx > 0 && prevRowColor != rowColor) 1091 RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); 1092 } 1093 } 1094 1095 // Check for many rows 1096 const uint32_t kMaxLayoutRows = 20; 1097 if (rowCount > kMaxLayoutRows) { // A ton of rows, this is probably for data 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099 } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 {

Slide 95

Slide 95 text

GDS @edds 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099 } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 { 1138 Relation rel = HyperTextAccessible::RelationByType(aType); 1139 if (aType == RelationType::LABEL_FOR) 1140 rel.AppendTarget(Parent()); 1141 1142 return rel; 1143 } 1144 1145 role 1146 HTMLCaptionAccessible::NativeRole() 1098 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1099 } 1100 1101 // Check for very wide table. 1102 nsIFrame* documentFrame = Document()->GetFrame(); 1103 nsSize documentSize = documentFrame->GetSize(); 1104 if (documentSize.width > 0) { 1105 nsSize tableSize = GetFrame()->GetSize(); 1106 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1107 if (percentageOfDocWidth > 95) { 1108 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1109 // Probably for layout 1110 RETURN_LAYOUT_ANSWER(true, 1111 "<= 4 columns, table width is 95% of document width"); 1112 } 1113 } 1114 1115 // Two column rules 1116 if (rowCount * colCount <= 10) { 1117 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1118 } 1119 1120 if (HasDescendant(NS_LITERAL_STRING("embed")) || 1121 HasDescendant(NS_LITERAL_STRING("object")) || 1122 HasDescendant(NS_LITERAL_STRING("applet")) || 1123 HasDescendant(NS_LITERAL_STRING("iframe"))) { 1124 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1125 } 1126 1127 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1128 } 1129 1130 1131 //////////////////////////////////////////////////////////////////////////////// 1132 // HTMLCaptionAccessible 1133 //////////////////////////////////////////////////////////////////////////////// 1134 1135 Relation 1136 HTMLCaptionAccessible::RelationByType(RelationType aType) 1137 { 1138 Relation rel = HyperTextAccessible::RelationByType(aType); 1139 if (aType == RelationType::LABEL_FOR) 1140 rel.AppendTarget(Parent()); 1141 1142 return rel; 1143 } 1144 1145 role 1146 HTMLCaptionAccessible::NativeRole()

Slide 96

Slide 96 text

So what does this all mean? GDS @edds

Slide 97

Slide 97 text

Accessibility is more than just markup

Slide 98

Slide 98 text

Listen to your websites

Slide 99

Slide 99 text

GDS @edds

Slide 100

Slide 100 text

Thank you!! ! ! Special thanks to: Alice Bartlet, Robin Whittleton, Tom Byers, Alex Muller, Léonie Watson, David Singleton, Gemma Leigh and Jake Archibald GDS @edds

Slide 101

Slide 101 text

Edd Sowden
 Senior Developer 
 Government Digital Service
 @edds