@surea11y/core 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/CHANGELOG.md +81 -7
  2. package/LICENSE +373 -21
  3. package/README.md +175 -35
  4. package/bin/surea11y-core.js +20 -0
  5. package/docs/API_STABILITY.md +27 -1
  6. package/docs/BINDING_AUTHORS_GUIDE.md +9 -9
  7. package/docs/CI_INTEGRATIONS.md +103 -0
  8. package/docs/ENGINE_OPTIONS.md +2 -0
  9. package/docs/I18N.md +12 -9
  10. package/docs/INTEGRATION.md +19 -1
  11. package/docs/LIMITATIONS.md +1 -1
  12. package/docs/OUTPUT_SCHEMA.md +1 -1
  13. package/docs/REPORT.md +1 -1
  14. package/docs/RULE_CATALOG.md +1 -1
  15. package/docs/SARIF.md +59 -0
  16. package/package.json +63 -18
  17. package/src/baseline.js +0 -0
  18. package/src/checks/automatic/area-alt-present.js +63 -31
  19. package/src/checks/automatic/aria-allowed-attr.js +204 -80
  20. package/src/checks/automatic/aria-allowed-role.js +23 -7
  21. package/src/checks/automatic/aria-braille-equivalent.js +34 -10
  22. package/src/checks/automatic/aria-conditional-attr.js +32 -14
  23. package/src/checks/automatic/aria-deprecated-role.js +26 -11
  24. package/src/checks/automatic/aria-hidden-body.js +48 -23
  25. package/src/checks/automatic/aria-hidden-focus.js +420 -66
  26. package/src/checks/automatic/aria-prohibited-attr.js +327 -60
  27. package/src/checks/automatic/aria-prohibited-children.js +111 -103
  28. package/src/checks/automatic/aria-required-attr.js +29 -15
  29. package/src/checks/automatic/aria-required-children.js +44 -24
  30. package/src/checks/automatic/aria-required-parent.js +64 -35
  31. package/src/checks/automatic/aria-role-name-present.js +49 -21
  32. package/src/checks/automatic/aria-roles-valid.js +24 -12
  33. package/src/checks/automatic/aria-valid-attr-value.js +46 -22
  34. package/src/checks/automatic/aria-valid-attr.js +19 -5
  35. package/src/checks/automatic/autocomplete-valid.js +76 -16
  36. package/src/checks/automatic/avoid-inline-spacing.js +23 -8
  37. package/src/checks/automatic/binary-control-name-present.js +62 -50
  38. package/src/checks/automatic/button-name-present.js +54 -24
  39. package/src/checks/automatic/bypass-blocks-present.js +51 -32
  40. package/src/checks/automatic/canvas-text-alternative-present.js +59 -26
  41. package/src/checks/automatic/combobox-name-present.js +40 -45
  42. package/src/checks/automatic/contrast-computable.js +363 -341
  43. package/src/checks/automatic/contrast-enhanced.js +489 -466
  44. package/src/checks/automatic/contrast-minimum.js +488 -465
  45. package/src/checks/automatic/css-orientation-lock.js +51 -35
  46. package/src/checks/automatic/definition-list-children-valid.js +46 -25
  47. package/src/checks/automatic/deprecated-elements-not-used.js +25 -9
  48. package/src/checks/automatic/dialog-name-present.js +47 -85
  49. package/src/checks/automatic/dlitem-parent-valid.js +25 -8
  50. package/src/checks/automatic/duplicate-id-aria.js +28 -9
  51. package/src/checks/automatic/embed-text-alternative-present.js +88 -35
  52. package/src/checks/automatic/form-control-programmatic-label-present.js +81 -196
  53. package/src/checks/automatic/form-control-single-label.js +50 -14
  54. package/src/checks/automatic/html-xml-lang-mismatch.js +36 -18
  55. package/src/checks/automatic/iframe-focusable-content.js +265 -22
  56. package/src/checks/automatic/iframe-name-present.js +33 -9
  57. package/src/checks/automatic/iframe-title-unique.js +32 -9
  58. package/src/checks/automatic/img-alt-present.js +54 -52
  59. package/src/checks/automatic/input-image-alt-present.js +141 -112
  60. package/src/checks/automatic/label-in-name.js +65 -41
  61. package/src/checks/automatic/language-page-present.js +111 -109
  62. package/src/checks/automatic/link-in-text-block.js +61 -19
  63. package/src/checks/automatic/link-name-present.js +47 -14
  64. package/src/checks/automatic/list-children-valid.js +40 -33
  65. package/src/checks/automatic/listbox-name-present.js +41 -19
  66. package/src/checks/automatic/listitem-parent-valid.js +48 -13
  67. package/src/checks/automatic/menuitem-name-present.js +41 -61
  68. package/src/checks/automatic/meta-refresh-no-exceptions.js +32 -11
  69. package/src/checks/automatic/meta-refresh-timing-absent.js +22 -6
  70. package/src/checks/automatic/meta-viewport-zoom-enabled.js +26 -7
  71. package/src/checks/automatic/meter-name-present.js +40 -36
  72. package/src/checks/automatic/nested-interactive-controls-absent.js +58 -15
  73. package/src/checks/automatic/object-text-alternative-present.js +93 -39
  74. package/src/checks/automatic/option-name-present.js +40 -21
  75. package/src/checks/automatic/page-title-present.js +19 -6
  76. package/src/checks/automatic/progressbar-name-present.js +49 -44
  77. package/src/checks/automatic/role-img-alt-present.js +211 -159
  78. package/src/checks/automatic/searchbox-name-present.js +41 -19
  79. package/src/checks/automatic/server-side-image-map-absent.js +27 -11
  80. package/src/checks/automatic/slider-name-present.js +42 -47
  81. package/src/checks/automatic/spinbutton-name-present.js +41 -19
  82. package/src/checks/automatic/summary-name-present.js +39 -17
  83. package/src/checks/automatic/svg-image-text-alternative-present.js +116 -47
  84. package/src/checks/automatic/svg-text-alternative-present.js +262 -230
  85. package/src/checks/automatic/tab-name-present.js +39 -60
  86. package/src/checks/automatic/table-headers-attr-valid.js +27 -10
  87. package/src/checks/automatic/table-th-has-data-cells.js +24 -8
  88. package/src/checks/automatic/target-size-minimum.js +123 -48
  89. package/src/checks/automatic/td-has-header.js +53 -12
  90. package/src/checks/automatic/textbox-name-present.js +41 -19
  91. package/src/checks/automatic/tooltip-name-present.js +39 -18
  92. package/src/checks/automatic/treeitem-name-present.js +40 -21
  93. package/src/checks/automatic/valid-lang.js +22 -6
  94. package/src/checks/automatic/video-poster-text-alternative-present.js +81 -36
  95. package/src/checks/manual/accesskeys-manual.js +17 -6
  96. package/src/checks/manual/area-alt-decorative-manual.js +194 -193
  97. package/src/checks/manual/area-alt-quality-manual.js +184 -141
  98. package/src/checks/manual/aria-checked-state-mismatch-manual.js +48 -34
  99. package/src/checks/manual/aria-text-manual.js +20 -11
  100. package/src/checks/manual/canvas-text-alternative-quality-manual.js +151 -114
  101. package/src/checks/manual/css-hidden-focus.js +375 -169
  102. package/src/checks/manual/embed-text-alternative-quality-manual.js +178 -162
  103. package/src/checks/manual/empty-heading-manual.js +41 -24
  104. package/src/checks/manual/empty-table-header-manual.js +69 -31
  105. package/src/checks/manual/focus-order-semantics-manual.js +60 -13
  106. package/src/checks/manual/form-control-programmatic-label-quality-manual.js +209 -246
  107. package/src/checks/manual/heading-order-manual.js +50 -8
  108. package/src/checks/manual/identical-links-same-purpose-manual.js +36 -12
  109. package/src/checks/manual/image-redundant-alt-manual.js +38 -8
  110. package/src/checks/manual/img-alt-decorative-manual.js +133 -96
  111. package/src/checks/manual/img-alt-quality-manual.js +178 -127
  112. package/src/checks/manual/input-image-alt-decorative-manual.js +127 -92
  113. package/src/checks/manual/input-image-alt-quality-manual.js +127 -92
  114. package/src/checks/manual/label-title-only-manual.js +44 -28
  115. package/src/checks/manual/landmark-banner-is-top-level-manual.js +95 -38
  116. package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +85 -32
  117. package/src/checks/manual/landmark-main-is-top-level-manual.js +69 -27
  118. package/src/checks/manual/landmark-no-duplicate-banner-manual.js +45 -33
  119. package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +43 -31
  120. package/src/checks/manual/landmark-no-duplicate-main-manual.js +27 -21
  121. package/src/checks/manual/landmark-one-main-manual.js +38 -43
  122. package/src/checks/manual/landmark-unique-manual.js +78 -67
  123. package/src/checks/manual/link-name-quality-manual.js +45 -12
  124. package/src/checks/manual/media-transcript-present-manual.js +37 -22
  125. package/src/checks/manual/meta-viewport-large-manual.js +19 -6
  126. package/src/checks/manual/mouse-only-event-handlers-manual.js +40 -11
  127. package/src/checks/manual/no-autoplay-audio-manual.js +22 -6
  128. package/src/checks/manual/object-text-alternative-quality-manual.js +177 -154
  129. package/src/checks/manual/p-as-heading-manual.js +24 -7
  130. package/src/checks/manual/page-has-heading-one-manual.js +42 -32
  131. package/src/checks/manual/page-title-patterns-manual.js +80 -50
  132. package/src/checks/manual/presentation-role-conflict-manual.js +101 -47
  133. package/src/checks/manual/region-manual.js +244 -60
  134. package/src/checks/manual/scope-attr-valid-manual.js +13 -4
  135. package/src/checks/manual/scrollable-region-focusable-manual.js +39 -11
  136. package/src/checks/manual/skip-link-manual.js +42 -18
  137. package/src/checks/manual/svg-text-alternative-quality-manual.js +208 -165
  138. package/src/checks/manual/tabindex-manual.js +13 -4
  139. package/src/checks/manual/table-duplicate-name-manual.js +22 -11
  140. package/src/checks/manual/table-fake-caption-manual.js +48 -10
  141. package/src/checks/manual/video-caption-manual.js +17 -4
  142. package/src/checks/manual-review.js +58 -12
  143. package/src/core.js +41705 -29650
  144. package/src/index.js +2 -0
  145. package/src/report.js +109 -47
  146. package/src/sarif.js +190 -0
  147. package/surea11y.browser.js +37774 -0
  148. package/bin/core.js +0 -348
  149. package/docs/CLI.md +0 -75
  150. package/src/catalogs/composites.wcag.js +0 -490
  151. package/src/checks/rules-and-tags.full.csv +0 -19
  152. package/src/checks/rules-and-tags.full.json +0 -259
  153. package/src/core/aria-helpers.js +0 -970
  154. package/src/core/contrast-helpers.js +0 -1147
  155. package/src/core/dom-helpers.js +0 -4235
  156. package/src/core/dom-runner.js +0 -671
  157. package/src/core/frame-messaging.js +0 -210
  158. package/src/core/frame-scan.js +0 -178
  159. package/src/core/rollup-composites.js +0 -135
  160. package/src/core/rule-meta.js +0 -159
  161. package/src/coverage/wcag-facets.js +0 -1079
  162. package/src/coverage/wcag-version-map.js +0 -84
  163. package/src/i18n/en.js +0 -923
  164. package/src/i18n/fr.js +0 -844
  165. package/src/policy/contracts.js +0 -18
  166. package/src/policy/resolvePolicy.js +0 -55
  167. package/src/policy/schemas/engine-options.schema.json +0 -103
  168. package/src/policy/schemas/policy-contract.schema.json +0 -40
@@ -1,10 +1,12 @@
1
+ /* SPDX-License-Identifier: MPL-2.0 */
2
+
1
3
  'use strict';
2
4
 
3
5
  /**
4
6
  * @check table-duplicate-name
5
7
  * @atomic true
6
8
  * @summary A table's caption must not duplicate its summary attribute
7
- * @standard Best Practices (a widely-used reference engine's classification; no formal WCAG Success Criterion — see ROADMAP.md Tier 1b)
9
+ * @standard Best Practices (no formal WCAG Success Criterion — see ROADMAP.md Tier 1b)
8
10
  * @applicability
9
11
  * Applies to <table> elements that have both a <caption> with text
10
12
  * content and a (deprecated but still encountered) summary attribute.
@@ -17,16 +19,16 @@
17
19
  * - Not WCAG-normative — authored as an advisory, cantTell-capped
18
20
  * `type: 'manual'` rule; see landmark-banner-is-top-level's
19
21
  * header comment for the shared rationale/precedent.
20
- * - Narrowly scoped to caption-vs-summary duplication specifically
21
- * (matching a reference engine's table-duplicate-name check), not a general "table
22
- * name quality" check.
22
+ * - Narrowly scoped to caption-vs-summary duplication specifically, not a
23
+ * general "table name quality" check.
23
24
  */
24
25
 
25
26
  const id = 'table-duplicate-name';
26
27
 
27
28
  const meta = {
28
29
  title: 'Table caption must not duplicate its summary attribute',
29
- description: 'Checks that a <table>\'s <caption> text is not identical to its (deprecated) summary attribute.',
30
+ description:
31
+ "Checks that a <table>'s <caption> text is not identical to its (deprecated) summary attribute.",
30
32
  i18n: {
31
33
  titleKey: 'tableDuplicateName_title',
32
34
  descriptionKey: 'tableDuplicateName_description'
@@ -43,13 +45,17 @@ const meta = {
43
45
  };
44
46
 
45
47
  function runInPage(ctx) {
46
- const { document, helpers, rule } = ctx;
48
+ const { helpers, rule } = ctx;
47
49
 
48
50
  function normalizeWs(s) {
49
- return String(s || '').replace(/\s+/g, ' ').trim();
51
+ return String(s || '')
52
+ .replace(/\s+/g, ' ')
53
+ .trim();
50
54
  }
51
55
 
52
- const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table[summary]') : helpers.queryAll('table[summary]');
56
+ const nodes = helpers.queryAllSmart
57
+ ? helpers.queryAllSmart('table[summary]')
58
+ : helpers.queryAll('table[summary]');
53
59
 
54
60
  const occurrences = [];
55
61
  let applicableCount = 0;
@@ -68,12 +74,12 @@ function runInPage(ctx) {
68
74
  if (captionText.toLowerCase() !== summary.toLowerCase()) continue;
69
75
 
70
76
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
71
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
77
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
72
78
 
73
79
  occurrences.push({
74
80
  selector: stableSelector,
75
81
  html,
76
- summary: 'This table\'s caption duplicates its summary attribute.',
82
+ summary: "This table's caption duplicates its summary attribute.",
77
83
  hint: 'Remove the redundant summary attribute, or make it provide different information than the caption.',
78
84
  i18n: {
79
85
  summaryKey: 'tableDuplicateName_summary_cantTell',
@@ -90,7 +96,12 @@ function runInPage(ctx) {
90
96
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
91
97
  }
92
98
  if (occurrences.length) {
93
- return { ruleId: rule.ruleId, outcome: 'cantTell', severity: rule.defaultSeverity || 'minor', occurrences };
99
+ return {
100
+ ruleId: rule.ruleId,
101
+ outcome: 'cantTell',
102
+ severity: rule.defaultSeverity || 'minor',
103
+ occurrences
104
+ };
94
105
  }
95
106
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
96
107
  }
@@ -1,3 +1,5 @@
1
+ /* SPDX-License-Identifier: MPL-2.0 */
2
+
1
3
  'use strict';
2
4
 
3
5
  /**
@@ -32,7 +34,8 @@ const id = 'table-fake-caption';
32
34
 
33
35
  const meta = {
34
36
  title: "A table's first row should not stand in for a real <caption>",
35
- description: 'Flags tables with no <caption> whose first row has a single non-empty cell while other rows have multiple cells, for manual review of whether that cell is acting as a fake caption.',
37
+ description:
38
+ 'Flags tables with no <caption> whose first row has a single non-empty cell while other rows have multiple cells, for manual review of whether that cell is acting as a fake caption.',
36
39
  i18n: {
37
40
  titleKey: 'tableFakeCaption_title',
38
41
  descriptionKey: 'tableFakeCaption_description'
@@ -41,7 +44,13 @@ const meta = {
41
44
  tags: ['wcag2a', 'wcag131', 'structure', 'atomic', 'manual'],
42
45
  wcagSc: ['1.3.1'],
43
46
  normativeMappings: [
44
- { standard: 'WCAG', version: '2.2', requirement: '1.3.1', title: 'Info and Relationships', conformanceLevel: 'A' }
47
+ {
48
+ standard: 'WCAG',
49
+ version: '2.2',
50
+ requirement: '1.3.1',
51
+ title: 'Info and Relationships',
52
+ conformanceLevel: 'A'
53
+ }
45
54
  ],
46
55
  defaultSeverity: 'minor',
47
56
  category: 'perceivable',
@@ -51,9 +60,24 @@ const meta = {
51
60
  };
52
61
 
53
62
  function runInPage(ctx) {
54
- const { document, helpers, rule } = ctx;
63
+ const { helpers, rule } = ctx;
64
+
65
+ function trim(v) {
66
+ return (v == null ? '' : String(v)).trim();
67
+ }
55
68
 
56
- function trim(v) { return (v == null ? '' : String(v)).trim(); }
69
+ const isAccTreeEligible =
70
+ helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
71
+
72
+ function isEligible(node) {
73
+ if (!isAccTreeEligible) return true;
74
+ try {
75
+ const r = isAccTreeEligible(node, ctx);
76
+ return !!(r && r.eligible);
77
+ } catch {
78
+ return true;
79
+ }
80
+ }
57
81
 
58
82
  const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('table') : helpers.queryAll('table');
59
83
 
@@ -66,29 +90,38 @@ function runInPage(ctx) {
66
90
  const hasCaption = !!(table.querySelector && table.querySelector('caption'));
67
91
  if (hasCaption) continue;
68
92
 
69
- const rows = Array.from(table.rows);
93
+ // An aria-hidden row (or cell) isn't part of the AT-perceived table
94
+ // structure at all -- it must not be treated as the table's "first
95
+ // row" (or counted toward a row's cell count) for this positional
96
+ // heuristic.
97
+ const rows = Array.from(table.rows).filter(isEligible);
70
98
  if (rows.length < 2) continue;
71
99
 
72
100
  applicableCount += 1;
73
101
 
74
102
  const firstRow = rows[0];
75
- const firstRowCells = Array.from(firstRow.cells || []);
103
+ const firstRowCells = Array.from(firstRow.cells || []).filter(isEligible);
76
104
  if (firstRowCells.length !== 1) continue;
77
105
 
78
106
  const candidateCell = firstRowCells[0];
79
107
  const candidateText = trim(candidateCell.textContent || '');
80
108
  if (!candidateText) continue;
81
109
 
82
- const hasMultiCellRow = rows.slice(1).some((r) => Array.from(r.cells || []).length > 1);
110
+ const hasMultiCellRow = rows
111
+ .slice(1)
112
+ .some((r) => Array.from(r.cells || []).filter(isEligible).length > 1);
83
113
  if (!hasMultiCellRow) continue;
84
114
 
85
115
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(table) : 'html';
86
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(table) : (table.outerHTML || '');
116
+ const html = helpers.getOuterHtmlSnippet
117
+ ? helpers.getOuterHtmlSnippet(table)
118
+ : table.outerHTML || '';
87
119
 
88
120
  const baseOccurrence = {
89
121
  selector: stableSelector,
90
122
  html,
91
- summary: 'This table has no <caption>, but its first row is a single cell sitting above multi-cell rows — it may be acting as a fake caption.',
123
+ summary:
124
+ 'This table has no <caption>, but its first row is a single cell sitting above multi-cell rows — it may be acting as a fake caption.',
92
125
  hint: 'If this cell is meant to describe the table, use a real <caption> element instead of a lone first-row cell.',
93
126
  i18n: {
94
127
  summaryKey: 'tableFakeCaption_summary_cantTell',
@@ -112,7 +145,12 @@ function runInPage(ctx) {
112
145
  }
113
146
 
114
147
  if (occurrences.length) {
115
- return { ruleId: rule.ruleId, outcome: 'cantTell', severity: rule.defaultSeverity || 'minor', occurrences };
148
+ return {
149
+ ruleId: rule.ruleId,
150
+ outcome: 'cantTell',
151
+ severity: rule.defaultSeverity || 'minor',
152
+ occurrences
153
+ };
116
154
  }
117
155
 
118
156
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
@@ -1,3 +1,5 @@
1
+ /* SPDX-License-Identifier: MPL-2.0 */
2
+
1
3
  'use strict';
2
4
 
3
5
  /**
@@ -41,7 +43,13 @@ const meta = {
41
43
  tags: ['wcag2a', 'wcag122', 'media', 'timebasedmedia', 'atomic', 'manual'],
42
44
  wcagSc: ['1.2.2'],
43
45
  normativeMappings: [
44
- { standard: 'WCAG', version: '2.2', requirement: '1.2.2', title: 'Captions (Prerecorded)', conformanceLevel: 'A' }
46
+ {
47
+ standard: 'WCAG',
48
+ version: '2.2',
49
+ requirement: '1.2.2',
50
+ title: 'Captions (Prerecorded)',
51
+ conformanceLevel: 'A'
52
+ }
45
53
  ],
46
54
  defaultSeverity: 'moderate',
47
55
  category: 'perceivable',
@@ -51,7 +59,7 @@ const meta = {
51
59
  };
52
60
 
53
61
  function runInPage(ctx) {
54
- const { document, helpers, rule } = ctx;
62
+ const { helpers, rule } = ctx;
55
63
 
56
64
  const nodes = helpers.queryAllSmart ? helpers.queryAllSmart('video') : helpers.queryAll('video');
57
65
 
@@ -77,7 +85,7 @@ function runInPage(ctx) {
77
85
  if (hasCaptionsTrack) continue;
78
86
 
79
87
  const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
80
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : (el.outerHTML || '');
88
+ const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
81
89
 
82
90
  const baseOccurrence = {
83
91
  selector: stableSelector,
@@ -106,7 +114,12 @@ function runInPage(ctx) {
106
114
  }
107
115
 
108
116
  if (occurrences.length) {
109
- return { ruleId: rule.ruleId, outcome: 'cantTell', severity: rule.defaultSeverity || 'moderate', occurrences };
117
+ return {
118
+ ruleId: rule.ruleId,
119
+ outcome: 'cantTell',
120
+ severity: rule.defaultSeverity || 'moderate',
121
+ occurrences
122
+ };
110
123
  }
111
124
 
112
125
  // Manual rules may only emit cantTell/notApplicable (never pass/fail):
@@ -1,3 +1,5 @@
1
+ /* SPDX-License-Identifier: MPL-2.0 */
2
+
1
3
  'use strict';
2
4
 
3
5
  /**
@@ -20,17 +22,59 @@ const meta = {
20
22
  type: 'manual',
21
23
  defaultConfidence: 'medium',
22
24
  normativeMappings: [
23
- { standard: 'WCAG', version: '2.2', requirement: '2.1.1', title: 'Keyboard', conformanceLevel: 'A', url: 'https://www.w3.org/TR/WCAG22/#keyboard' },
24
- { standard: 'WCAG', version: '2.2', requirement: '2.4.3', title: 'Focus Order', conformanceLevel: 'A', url: 'https://www.w3.org/TR/WCAG22/#focus-order' },
25
- { standard: 'WCAG', version: '2.2', requirement: '2.4.7', title: 'Focus Visible', conformanceLevel: 'AA', url: 'https://www.w3.org/TR/WCAG22/#focus-visible' },
25
+ {
26
+ standard: 'WCAG',
27
+ version: '2.2',
28
+ requirement: '2.1.1',
29
+ title: 'Keyboard',
30
+ conformanceLevel: 'A',
31
+ url: 'https://www.w3.org/TR/WCAG22/#keyboard'
32
+ },
33
+ {
34
+ standard: 'WCAG',
35
+ version: '2.2',
36
+ requirement: '2.4.3',
37
+ title: 'Focus Order',
38
+ conformanceLevel: 'A',
39
+ url: 'https://www.w3.org/TR/WCAG22/#focus-order'
40
+ },
41
+ {
42
+ standard: 'WCAG',
43
+ version: '2.2',
44
+ requirement: '2.4.7',
45
+ title: 'Focus Visible',
46
+ conformanceLevel: 'AA',
47
+ url: 'https://www.w3.org/TR/WCAG22/#focus-visible'
48
+ },
26
49
 
27
50
  { standard: 'EN 301 549', version: 'V3.2.1', requirement: '9.2.1.1', title: 'Keyboard' },
28
51
  { standard: 'EN 301 549', version: 'V3.2.1', requirement: '9.2.4.3', title: 'Focus Order' },
29
52
  { standard: 'EN 301 549', version: 'V3.2.1', requirement: '9.2.4.7', title: 'Focus Visible' },
30
53
 
31
- { standard: 'WCAG', version: '2.2', type: 'Understanding', requirement: '2.1.1', title: 'Understanding Keyboard', url: 'https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html' },
32
- { standard: 'WCAG', version: '2.2', type: 'Understanding', requirement: '2.4.3', title: 'Understanding Focus Order', url: 'https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html' },
33
- { standard: 'WCAG', version: '2.2', type: 'Understanding', requirement: '2.4.7', title: 'Understanding Focus Visible', url: 'https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html' }
54
+ {
55
+ standard: 'WCAG',
56
+ version: '2.2',
57
+ type: 'Understanding',
58
+ requirement: '2.1.1',
59
+ title: 'Understanding Keyboard',
60
+ url: 'https://www.w3.org/WAI/WCAG22/Understanding/keyboard.html'
61
+ },
62
+ {
63
+ standard: 'WCAG',
64
+ version: '2.2',
65
+ type: 'Understanding',
66
+ requirement: '2.4.3',
67
+ title: 'Understanding Focus Order',
68
+ url: 'https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html'
69
+ },
70
+ {
71
+ standard: 'WCAG',
72
+ version: '2.2',
73
+ type: 'Understanding',
74
+ requirement: '2.4.7',
75
+ title: 'Understanding Focus Visible',
76
+ url: 'https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html'
77
+ }
34
78
  ]
35
79
  };
36
80
 
@@ -43,7 +87,8 @@ const meta = {
43
87
  function runInPage(ctx) {
44
88
  const { document, root, helpers, rule } = ctx;
45
89
 
46
- const getOuterHtmlSnippet = helpers && helpers.getOuterHtmlSnippet
90
+ const getOuterHtmlSnippet =
91
+ helpers && helpers.getOuterHtmlSnippet
47
92
  ? helpers.getOuterHtmlSnippet
48
93
  : (el) => (el && el.outerHTML) || '';
49
94
 
@@ -66,10 +111,7 @@ function runInPage(ctx) {
66
111
  }
67
112
 
68
113
  const fallbackRoot =
69
- rootEl ||
70
- document.documentElement ||
71
- document.body ||
72
- document.querySelector('html');
114
+ rootEl || document.documentElement || document.body || document.querySelector('html');
73
115
 
74
116
  const html = getOuterHtmlSnippet(fallbackRoot);
75
117
 
@@ -82,7 +124,11 @@ function runInPage(ctx) {
82
124
  selector: contextSelector || 'html',
83
125
  html,
84
126
  summary: 'Manual review required for keyboard navigation and focus order.',
85
- i18n: { summaryKey: 'manualReview_summary_cantTell', hintKey: 'manualReview_hint_cantTell', params: {} },
127
+ i18n: {
128
+ summaryKey: 'manualReview_summary_cantTell',
129
+ hintKey: 'manualReview_hint_cantTell',
130
+ params: {}
131
+ }
86
132
  }
87
133
  ]
88
134
  };