@surea11y/core 1.3.0 → 1.4.1

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 (166) hide show
  1. package/CHANGELOG.md +87 -2
  2. package/README.md +109 -35
  3. package/bin/surea11y-core.js +20 -0
  4. package/docs/API_STABILITY.md +26 -0
  5. package/docs/ARIA_DEPRECATION.md +95 -0
  6. package/docs/CI_INTEGRATIONS.md +7 -7
  7. package/docs/ENGINE_OPTIONS.md +1 -1
  8. package/docs/I18N.md +12 -9
  9. package/docs/INTEGRATION.md +1 -1
  10. package/docs/LIMITATIONS.md +1 -1
  11. package/docs/REPORT.md +1 -1
  12. package/docs/RULE_CATALOG.md +6 -6
  13. package/package.json +52 -16
  14. package/src/baseline.js +0 -0
  15. package/src/checks/automatic/area-alt-present.js +4 -6
  16. package/src/checks/automatic/aria-allowed-attr.js +663 -134
  17. package/src/checks/automatic/aria-allowed-role.js +2 -0
  18. package/src/checks/automatic/aria-braille-equivalent.js +2 -0
  19. package/src/checks/automatic/aria-conditional-attr.js +8 -7
  20. package/src/checks/automatic/aria-deprecated-role.js +107 -38
  21. package/src/checks/automatic/aria-hidden-body.js +6 -4
  22. package/src/checks/automatic/aria-hidden-focus.js +12 -13
  23. package/src/checks/automatic/aria-prohibited-attr.js +98 -105
  24. package/src/checks/automatic/aria-prohibited-children.js +56 -87
  25. package/src/checks/automatic/aria-required-attr.js +6 -7
  26. package/src/checks/automatic/aria-required-children.js +7 -10
  27. package/src/checks/automatic/aria-required-parent.js +20 -25
  28. package/src/checks/automatic/aria-role-name-present.js +2 -0
  29. package/src/checks/automatic/aria-roles-valid.js +33 -6
  30. package/src/checks/automatic/aria-valid-attr-value.js +18 -15
  31. package/src/checks/automatic/aria-valid-attr.js +2 -0
  32. package/src/checks/automatic/autocomplete-valid.js +39 -1
  33. package/src/checks/automatic/avoid-inline-spacing.js +181 -20
  34. package/src/checks/automatic/binary-control-name-present.js +13 -3
  35. package/src/checks/automatic/button-name-present.js +54 -21
  36. package/src/checks/automatic/canvas-text-alternative-present.js +17 -8
  37. package/src/checks/automatic/combobox-name-present.js +10 -1
  38. package/src/checks/automatic/contrast-computable.js +2 -0
  39. package/src/checks/automatic/contrast-enhanced.js +2 -0
  40. package/src/checks/automatic/contrast-minimum.js +2 -0
  41. package/src/checks/automatic/css-orientation-lock.js +21 -27
  42. package/src/checks/automatic/definition-list-children-valid.js +6 -6
  43. package/src/checks/automatic/deprecated-elements-not-used.js +4 -2
  44. package/src/checks/automatic/dialog-name-present.js +18 -11
  45. package/src/checks/automatic/dlitem-parent-valid.js +2 -0
  46. package/src/checks/automatic/duplicate-id-aria.js +4 -3
  47. package/src/checks/automatic/embed-text-alternative-present.js +2 -0
  48. package/src/checks/automatic/form-control-programmatic-label-present.js +50 -4
  49. package/src/checks/automatic/form-control-single-label.js +110 -43
  50. package/src/checks/automatic/html-xml-lang-mismatch.js +2 -0
  51. package/src/checks/automatic/iframe-focusable-content.js +246 -18
  52. package/src/checks/automatic/iframe-name-present.js +2 -0
  53. package/src/checks/automatic/iframe-title-unique.js +3 -1
  54. package/src/checks/automatic/img-alt-present.js +23 -18
  55. package/src/checks/automatic/input-image-alt-present.js +101 -52
  56. package/src/checks/automatic/label-in-name.js +97 -27
  57. package/src/checks/automatic/language-page-present.js +7 -1
  58. package/src/checks/automatic/link-in-text-block.js +2 -0
  59. package/src/checks/automatic/link-name-present.js +52 -17
  60. package/src/checks/automatic/list-children-valid.js +14 -24
  61. package/src/checks/automatic/listbox-name-present.js +10 -1
  62. package/src/checks/automatic/listitem-parent-valid.js +30 -7
  63. package/src/checks/automatic/menuitem-name-present.js +10 -1
  64. package/src/checks/automatic/meta-refresh-no-exceptions.js +33 -4
  65. package/src/checks/automatic/meta-refresh-timing-absent.js +32 -4
  66. package/src/checks/automatic/meta-viewport-zoom-enabled.js +39 -15
  67. package/src/checks/automatic/meter-name-present.js +12 -4
  68. package/src/checks/automatic/nested-interactive-controls-absent.js +177 -25
  69. package/src/checks/automatic/object-text-alternative-present.js +16 -7
  70. package/src/checks/automatic/option-name-present.js +10 -1
  71. package/src/checks/automatic/page-title-present.js +2 -0
  72. package/src/checks/automatic/progressbar-name-present.js +16 -11
  73. package/src/checks/automatic/role-img-alt-present.js +4 -4
  74. package/src/checks/automatic/searchbox-name-present.js +10 -1
  75. package/src/checks/automatic/server-side-image-map-absent.js +4 -3
  76. package/src/checks/automatic/slider-name-present.js +13 -2
  77. package/src/checks/automatic/spinbutton-name-present.js +10 -1
  78. package/src/checks/automatic/summary-name-present.js +10 -1
  79. package/src/checks/automatic/svg-image-text-alternative-present.js +2 -0
  80. package/src/checks/automatic/svg-text-alternative-present.js +17 -5
  81. package/src/checks/automatic/tab-name-present.js +10 -1
  82. package/src/checks/automatic/table-headers-attr-valid.js +3 -2
  83. package/src/checks/automatic/table-th-has-data-cells.js +69 -6
  84. package/src/checks/automatic/target-size-minimum.js +5 -0
  85. package/src/checks/automatic/td-has-header.js +24 -1
  86. package/src/checks/automatic/textbox-name-present.js +10 -1
  87. package/src/checks/automatic/tooltip-name-present.js +10 -1
  88. package/src/checks/automatic/treeitem-name-present.js +10 -1
  89. package/src/checks/automatic/valid-lang.js +18 -3
  90. package/src/checks/automatic/video-poster-text-alternative-present.js +2 -0
  91. package/src/checks/manual/accesskeys-manual.js +3 -1
  92. package/src/checks/manual/area-alt-decorative-manual.js +2 -0
  93. package/src/checks/manual/area-alt-quality-manual.js +2 -0
  94. package/src/checks/manual/aria-checked-state-mismatch-manual.js +14 -23
  95. package/src/checks/manual/aria-text-manual.js +6 -5
  96. package/src/checks/manual/bypass-blocks-present-manual.js +279 -0
  97. package/src/checks/manual/canvas-text-alternative-quality-manual.js +2 -0
  98. package/src/checks/manual/css-hidden-focus.js +184 -9
  99. package/src/checks/manual/embed-text-alternative-quality-manual.js +18 -13
  100. package/src/checks/manual/empty-heading-manual.js +17 -17
  101. package/src/checks/manual/empty-table-header-manual.js +52 -25
  102. package/src/checks/manual/focus-order-semantics-manual.js +16 -4
  103. package/src/checks/manual/form-control-programmatic-label-quality-manual.js +2 -0
  104. package/src/checks/manual/heading-order-manual.js +28 -1
  105. package/src/checks/manual/identical-links-same-purpose-manual.js +2 -0
  106. package/src/checks/manual/image-redundant-alt-manual.js +21 -1
  107. package/src/checks/manual/img-alt-decorative-manual.js +2 -0
  108. package/src/checks/manual/img-alt-quality-manual.js +2 -0
  109. package/src/checks/manual/input-image-alt-decorative-manual.js +26 -0
  110. package/src/checks/manual/input-image-alt-quality-manual.js +2 -0
  111. package/src/checks/manual/label-title-only-manual.js +29 -22
  112. package/src/checks/manual/landmark-banner-is-top-level-manual.js +51 -55
  113. package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +44 -31
  114. package/src/checks/manual/landmark-main-is-top-level-manual.js +41 -24
  115. package/src/checks/manual/landmark-no-duplicate-banner-manual.js +16 -23
  116. package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +14 -21
  117. package/src/checks/manual/landmark-no-duplicate-main-manual.js +10 -13
  118. package/src/checks/manual/landmark-one-main-manual.js +12 -23
  119. package/src/checks/manual/landmark-unique-manual.js +37 -52
  120. package/src/checks/manual/link-name-quality-manual.js +2 -0
  121. package/src/checks/manual/media-transcript-present-manual.js +2 -0
  122. package/src/checks/manual/meta-viewport-large-manual.js +3 -1
  123. package/src/checks/manual/mouse-only-event-handlers-manual.js +2 -0
  124. package/src/checks/manual/no-autoplay-audio-manual.js +2 -0
  125. package/src/checks/manual/object-text-alternative-quality-manual.js +2 -0
  126. package/src/checks/manual/p-as-heading-manual.js +2 -0
  127. package/src/checks/manual/page-has-heading-one-manual.js +12 -11
  128. package/src/checks/manual/page-title-patterns-manual.js +2 -0
  129. package/src/checks/manual/presentation-role-conflict-manual.js +51 -37
  130. package/src/checks/manual/region-manual.js +27 -36
  131. package/src/checks/manual/scope-attr-valid-manual.js +3 -1
  132. package/src/checks/manual/scrollable-region-focusable-manual.js +2 -0
  133. package/src/checks/manual/skip-link-manual.js +7 -6
  134. package/src/checks/manual/svg-text-alternative-quality-manual.js +2 -0
  135. package/src/checks/manual/tabindex-manual.js +3 -1
  136. package/src/checks/manual/table-duplicate-name-manual.js +5 -4
  137. package/src/checks/manual/table-fake-caption-manual.js +24 -3
  138. package/src/checks/manual/video-caption-manual.js +2 -0
  139. package/src/checks/manual-review.js +2 -0
  140. package/src/core.js +11820 -3317
  141. package/src/index.js +2 -0
  142. package/src/report.js +51 -9
  143. package/src/sarif.js +20 -5
  144. package/surea11y.browser.js +4943 -1388
  145. package/bin/core.js +0 -473
  146. package/docs/CLI.md +0 -128
  147. package/src/catalogs/composites.wcag.js +0 -454
  148. package/src/checks/automatic/bypass-blocks-present.js +0 -215
  149. package/src/checks/rules-and-tags.full.csv +0 -19
  150. package/src/checks/rules-and-tags.full.json +0 -259
  151. package/src/core/aria-helpers.js +0 -1211
  152. package/src/core/contrast-helpers.js +0 -1302
  153. package/src/core/dom-helpers.js +0 -4493
  154. package/src/core/dom-runner.js +0 -787
  155. package/src/core/frame-messaging.js +0 -261
  156. package/src/core/frame-scan.js +0 -190
  157. package/src/core/rollup-composites.js +0 -127
  158. package/src/core/rule-meta.js +0 -176
  159. package/src/coverage/wcag-facets.js +0 -1079
  160. package/src/coverage/wcag-version-map.js +0 -84
  161. package/src/i18n/en.js +0 -1228
  162. package/src/i18n/fr.js +0 -1185
  163. package/src/policy/contracts.js +0 -18
  164. package/src/policy/resolvePolicy.js +0 -59
  165. package/src/policy/schemas/engine-options.schema.json +0 -103
  166. package/src/policy/schemas/policy-contract.schema.json +0 -40
@@ -1,454 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * Composite rules catalog (human-facing).
5
- *
6
- * A composite rule groups existing atomic checks (checks) by listing their ids in `checksIds`.
7
- *
8
- * For now, composites are grouped per WCAG Success Criterion (SC).
9
- */
10
- module.exports = [
11
- {
12
- id: 'wcag-1.1.1-non-text-content',
13
- checksIds: [
14
- 'area-alt-present',
15
- 'img-alt-decorative',
16
- 'img-alt-quality',
17
- 'area-alt-decorative',
18
- 'area-alt-quality',
19
- 'canvas-text-alternative-quality',
20
- 'canvas-text-alternative-present',
21
- 'embed-text-alternative-present',
22
- 'img-alt-present',
23
- 'input-image-alt-present',
24
- 'object-text-alternative-present',
25
- 'role-img-text-alternative-present',
26
- 'svg-image-text-alternative-present',
27
- 'svg-text-alternative-present',
28
- 'video-poster-text-alternative-present',
29
- 'embed-text-alternative-quality',
30
- 'input-image-alt-decorative',
31
- 'input-image-alt-quality',
32
- 'object-text-alternative-quality',
33
- 'svg-text-alternative-quality',
34
- 'meter-name-present',
35
- 'progressbar-name-present'
36
- ],
37
- meta: {
38
- title: 'Non-text content: text alternatives',
39
- description:
40
- 'Rollup of checks ensuring non-text content has an appropriate text alternative.',
41
- wcagSc: ['1.1.1'],
42
- level: 'A'
43
- }
44
- },
45
-
46
- {
47
- id: 'wcag-1.2.1-audio-only-video-only-prerecorded',
48
- checksIds: ['media-alternative-transcript-evidence'],
49
- meta: {
50
- title: 'Audio-only and video-only (prerecorded): transcript',
51
- description:
52
- 'Rollup of checks for transcript availability for prerecorded audio-only/video-only media.',
53
- wcagSc: ['1.2.1'],
54
- level: 'A'
55
- }
56
- },
57
-
58
- {
59
- id: 'wcag-1.2.2-captions-prerecorded',
60
- checksIds: ['video-caption'],
61
- meta: {
62
- title: 'Captions (Prerecorded)',
63
- description: 'Rollup of checks for captions-track evidence on prerecorded video.',
64
- wcagSc: ['1.2.2'],
65
- level: 'A'
66
- }
67
- },
68
-
69
- {
70
- id: 'wcag-1.3.1-info-and-relationships',
71
- checksIds: [
72
- 'table-headers-attr-valid',
73
- 'table-th-has-data-cells',
74
- 'aria-hidden-body',
75
- 'list-children-valid',
76
- 'listitem-parent-valid',
77
- 'definition-list-children-valid',
78
- 'dlitem-parent-valid',
79
- 'form-control-programmatic-label-present',
80
- 'p-as-heading',
81
- 'table-fake-caption',
82
- 'td-has-header'
83
- ],
84
- meta: {
85
- title: 'Info and Relationships',
86
- description:
87
- 'Rollup of checks ensuring information, structure, and relationships conveyed through presentation are programmatically determinable.',
88
- wcagSc: ['1.3.1'],
89
- level: 'A'
90
- }
91
- },
92
-
93
- {
94
- id: 'wcag-1.4.1-use-of-color',
95
- checksIds: ['link-in-text-block'],
96
- meta: {
97
- title: 'Use of Color',
98
- description:
99
- 'Rollup of checks ensuring color is not used as the only visual means of conveying information.',
100
- wcagSc: ['1.4.1'],
101
- level: 'A'
102
- }
103
- },
104
-
105
- {
106
- id: 'wcag-1.4.2-audio-control',
107
- checksIds: ['no-autoplay-audio'],
108
- meta: {
109
- title: 'Audio Control',
110
- description:
111
- 'Rollup of checks for a pause/stop or volume-control mechanism on autoplaying audio.',
112
- wcagSc: ['1.4.2'],
113
- level: 'A'
114
- }
115
- },
116
-
117
- {
118
- id: 'wcag-1.4.3-contrast-minimum',
119
- checksIds: ['contrast-computable', 'contrast-minimum'],
120
- meta: {
121
- title: 'Contrast: minimum',
122
- description: 'Rollup of checks for minimum text contrast.',
123
- wcagSc: ['1.4.3'],
124
- level: 'AA'
125
- }
126
- },
127
-
128
- {
129
- id: 'wcag-1.4.6-contrast-enhanced',
130
- checksIds: ['contrast-computable', 'contrast-enhanced'],
131
- meta: {
132
- title: 'Contrast: enhanced',
133
- description: 'Rollup of checks for enhanced text contrast.',
134
- wcagSc: ['1.4.6'],
135
- level: 'AAA'
136
- }
137
- },
138
-
139
- {
140
- id: 'wcag-2.1.1-keyboard',
141
- checksIds: [
142
- 'manual-review',
143
- 'iframe-focusable-content',
144
- 'server-side-image-map-absent',
145
- 'scrollable-region-focusable',
146
- 'mouse-only-event-handlers'
147
- ],
148
- meta: {
149
- title: 'Keyboard',
150
- description:
151
- 'Rollup of checks ensuring functionality is operable through a keyboard interface.',
152
- wcagSc: ['2.1.1'],
153
- level: 'A'
154
- }
155
- },
156
-
157
- {
158
- id: 'wcag-2.1.3-keyboard-no-exception',
159
- checksIds: ['scrollable-region-focusable'],
160
- meta: {
161
- title: 'Keyboard (No Exception)',
162
- description:
163
- 'Rollup of checks ensuring functionality is operable through a keyboard interface with no exceptions (AAA).',
164
- wcagSc: ['2.1.3'],
165
- level: 'AAA'
166
- }
167
- },
168
-
169
- {
170
- id: 'wcag-2.2.2-pause-stop-hide',
171
- checksIds: ['deprecated-elements-not-used'],
172
- meta: {
173
- title: 'Pause, Stop, Hide',
174
- description:
175
- 'Rollup of checks ensuring moving, blinking, or auto-scrolling content can be paused, stopped, or hidden.',
176
- wcagSc: ['2.2.2'],
177
- level: 'A'
178
- }
179
- },
180
-
181
- {
182
- id: 'wcag-2.4.1-bypass-blocks',
183
- checksIds: ['bypass-blocks-present'],
184
- meta: {
185
- title: 'Bypass Blocks',
186
- description:
187
- 'Rollup of checks ensuring the page provides a way to bypass repeated blocks of content.',
188
- wcagSc: ['2.4.1'],
189
- level: 'A'
190
- }
191
- },
192
-
193
- {
194
- id: 'wcag-2.4.2-page-titled',
195
- checksIds: ['page-title-patterns', 'page-title-present'],
196
- meta: {
197
- title: 'Page titled',
198
- description: 'Rollup of checks ensuring documents have a meaningful page title.',
199
- wcagSc: ['2.4.2'],
200
- level: 'A'
201
- }
202
- },
203
-
204
- {
205
- id: 'wcag-2.4.3-focus-order',
206
- checksIds: ['manual-review'],
207
- meta: {
208
- title: 'Focus order',
209
- description: 'Rollup of checks ensuring focus moves through content in a meaningful order.',
210
- wcagSc: ['2.4.3'],
211
- level: 'A'
212
- }
213
- },
214
-
215
- {
216
- id: 'wcag-2.4.4-link-purpose-in-context',
217
- checksIds: ['link-name-quality'],
218
- meta: {
219
- title: 'Link Purpose (In Context)',
220
- description:
221
- 'Rollup of checks flagging links whose text alone is a known non-descriptive/generic phrase.',
222
- wcagSc: ['2.4.4'],
223
- level: 'A'
224
- }
225
- },
226
-
227
- {
228
- id: 'wcag-2.4.7-focus-visible',
229
- checksIds: ['aria-hidden-focus', 'css-hidden-focus', 'manual-review'],
230
- meta: {
231
- title: 'Focus visible',
232
- description:
233
- 'Rollup of checks ensuring keyboard focus is not hidden and remains perceivable.',
234
- wcagSc: ['2.4.7'],
235
- level: 'AA'
236
- }
237
- },
238
-
239
- {
240
- id: 'wcag-2.4.9-link-purpose-link-only',
241
- checksIds: ['identical-links-same-purpose'],
242
- meta: {
243
- title: 'Link Purpose (Link Only)',
244
- description:
245
- 'Rollup of checks ensuring links with the same accessible name serve the same purpose (AAA).',
246
- wcagSc: ['2.4.9'],
247
- level: 'AAA'
248
- }
249
- },
250
- {
251
- id: 'wcag-2.5.3-label-in-name',
252
- checksIds: ['label-in-name'],
253
- meta: {
254
- title: 'Label in name',
255
- description:
256
- 'Rollup of checks ensuring that when a control has a visible text label, the accessible name contains that visible label text.',
257
- wcagSc: ['2.5.3'],
258
- level: 'A'
259
- }
260
- },
261
- {
262
- id: 'wcag-2.5.8-target-size-minimum',
263
- checksIds: ['target-size-minimum'],
264
- meta: {
265
- title: 'Target size: minimum',
266
- description: 'Rollup of checks ensuring pointer targets meet minimum size requirements.',
267
- wcagSc: ['2.5.8'],
268
- level: 'AA'
269
- }
270
- },
271
-
272
- {
273
- id: 'wcag-3.1.1-language-of-page',
274
- checksIds: ['html-lang-attr-present', 'html-xml-lang-mismatch'],
275
- meta: {
276
- title: 'Language of page',
277
- description: 'Rollup of checks ensuring the page language is specified.',
278
- wcagSc: ['3.1.1'],
279
- level: 'A'
280
- }
281
- },
282
-
283
- {
284
- id: 'wcag-3.1.2-language-of-parts',
285
- checksIds: ['valid-lang'],
286
- meta: {
287
- title: 'Language of Parts',
288
- description:
289
- 'Rollup of checks ensuring elements whose language differs from the page default declare it correctly.',
290
- wcagSc: ['3.1.2'],
291
- level: 'AA'
292
- }
293
- },
294
-
295
- {
296
- id: 'wcag-1.3.4-orientation',
297
- checksIds: ['css-orientation-lock'],
298
- meta: {
299
- title: 'Orientation',
300
- description:
301
- 'Rollup of checks ensuring content does not restrict its view to a single display orientation.',
302
- wcagSc: ['1.3.4'],
303
- level: 'AA'
304
- }
305
- },
306
-
307
- {
308
- id: 'wcag-1.3.5-identify-input-purpose',
309
- checksIds: ['autocomplete-valid'],
310
- meta: {
311
- title: 'Identify Input Purpose',
312
- description:
313
- 'Rollup of checks ensuring the autocomplete attribute correctly identifies input purpose.',
314
- wcagSc: ['1.3.5'],
315
- level: 'AA'
316
- }
317
- },
318
-
319
- {
320
- id: 'wcag-1.4.12-text-spacing',
321
- checksIds: ['avoid-inline-spacing'],
322
- meta: {
323
- title: 'Text Spacing',
324
- description:
325
- 'Rollup of checks ensuring inline styles do not block user text-spacing overrides.',
326
- wcagSc: ['1.4.12'],
327
- level: 'AA'
328
- }
329
- },
330
-
331
- {
332
- id: 'wcag-2.2.4-interruptions',
333
- checksIds: ['meta-refresh-no-exceptions'],
334
- meta: {
335
- title: 'Interruptions',
336
- description:
337
- "Rollup of checks ensuring automatic context changes only happen at the user's request (AAA).",
338
- wcagSc: ['2.2.4'],
339
- level: 'AAA'
340
- }
341
- },
342
-
343
- {
344
- id: 'wcag-3.2.5-change-on-request',
345
- checksIds: ['meta-refresh-no-exceptions'],
346
- meta: {
347
- title: 'Change on Request',
348
- description:
349
- "Rollup of checks ensuring context changes only happen at the user's request (AAA).",
350
- wcagSc: ['3.2.5'],
351
- level: 'AAA'
352
- }
353
- },
354
-
355
- {
356
- id: 'wcag-4.1.2-name',
357
- checksIds: [
358
- 'aria-role-name-present',
359
- 'binary-control-name-present',
360
- 'button-name-present',
361
- 'combobox-name-present',
362
- 'dialog-name-present',
363
- 'form-control-programmatic-label-present',
364
- 'iframe-name-present',
365
- 'iframe-title-unique',
366
- 'link-name-present',
367
- 'listbox-name-present',
368
- 'menuitem-name-present',
369
- 'option-name-present',
370
- 'searchbox-name-present',
371
- 'slider-name-present',
372
- 'spinbutton-name-present',
373
- 'tab-name-present',
374
- 'textbox-name-present',
375
- 'treeitem-name-present',
376
- 'aria-hidden-focus',
377
- 'aria-hidden-body',
378
- 'form-control-programmatic-label-quality',
379
- 'summary-name-present',
380
- 'tooltip-name-present'
381
- ],
382
- meta: {
383
- title: 'Name, role, value: accessible name',
384
- description:
385
- 'Rollup of checks that common interactive elements expose a non-empty accessible name.',
386
- wcagSc: ['4.1.2'],
387
- level: 'A'
388
- }
389
- },
390
-
391
- {
392
- id: 'wcag-4.1.2-aria-validity',
393
- checksIds: [
394
- 'aria-roles-valid',
395
- 'aria-deprecated-role',
396
- 'aria-valid-attr',
397
- 'aria-valid-attr-value',
398
- 'aria-allowed-attr',
399
- 'aria-prohibited-attr',
400
- 'aria-required-attr',
401
- 'aria-allowed-role',
402
- 'aria-required-children',
403
- 'aria-prohibited-children',
404
- 'aria-required-parent',
405
- 'duplicate-id-aria',
406
- 'nested-interactive-controls-absent',
407
- 'aria-braille-equivalent',
408
- 'aria-conditional-attr',
409
- 'aria-checked-state-mismatch'
410
- ],
411
- meta: {
412
- title: 'Name, role, value: ARIA validity',
413
- description:
414
- 'Rollup of checks that ARIA role and attribute usage conforms to the WAI-ARIA specification (valid roles, valid attributes, valid values, required attributes/relationships, unique ARIA-referenced ids).',
415
- wcagSc: ['4.1.2'],
416
- level: 'A'
417
- }
418
- },
419
-
420
- {
421
- id: 'wcag-1.4.4-resize-text',
422
- checksIds: ['meta-viewport-zoom-enabled'],
423
- meta: {
424
- title: 'Resize Text',
425
- description:
426
- 'Rollup of checks ensuring the viewport meta tag does not prevent users from zooming text up to 200%.',
427
- wcagSc: ['1.4.4'],
428
- level: 'AA'
429
- }
430
- },
431
-
432
- {
433
- id: 'wcag-2.2.1-timing-adjustable',
434
- checksIds: ['meta-refresh-timing-absent'],
435
- meta: {
436
- title: 'Timing Adjustable',
437
- description:
438
- 'Rollup of checks ensuring the page does not impose a timed refresh the user cannot control.',
439
- wcagSc: ['2.2.1'],
440
- level: 'A'
441
- }
442
- },
443
-
444
- {
445
- id: 'wcag-3.3.2-labels-or-instructions',
446
- checksIds: ['form-control-single-label', 'form-control-programmatic-label-present'],
447
- meta: {
448
- title: 'Labels or Instructions',
449
- description: 'Rollup of checks ensuring form controls have unambiguous labeling.',
450
- wcagSc: ['3.3.2'],
451
- level: 'A'
452
- }
453
- }
454
- ];
@@ -1,215 +0,0 @@
1
- 'use strict';
2
-
3
- /**
4
- * @check bypass-blocks-present
5
- * @atomic true
6
- * @summary The page must provide at least one mechanism to bypass repeated blocks of content
7
- * @standard WCAG 2.2
8
- * @sc 2.4.1
9
- * @applicability
10
- * Always applicable to any HTML document with a <body> element —
11
- * "bypass blocks" is a whole-page concern, matching
12
- * aria-hidden-body / page-title-present's pattern of
13
- * evaluating the document directly rather than a scoped root.
14
- * @expectation
15
- * At least one of the following recognized WCAG 2.4.1 techniques is
16
- * present:
17
- * (a) a main landmark (<main> or [role="main"]) — technique ARIA11: a
18
- * screen reader user can jump straight to it, bypassing everything
19
- * before it (nav, header, repeated blocks) in one step;
20
- * (b) a working same-page anchor link — technique G1/G123: an
21
- * <a href="#id"> (or legacy <a name="id">) whose target resolves to
22
- * a real element anywhere in the document. Deliberately NOT
23
- * required to be positioned before a <nav> or be keyboard-focus-
24
- * order-first — see implementation notes;
25
- * (c) at least one heading (<h1>-<h6> or [role="heading"]) — technique
26
- * H69: heading navigation is itself a standards-recognized bypass
27
- * mechanism (e.g. a screen reader's "jump by heading" command).
28
- * @implementation-notes
29
- * - This rule intentionally checks presence, not position, for the
30
- * same-page-anchor condition (b): a widely-used reference engine's real bypass algorithm is
31
- * heuristic (see ROADMAP.md's Tier 1a note on why this rule was
32
- * deferred from the rest of that batch), and getting DOM-order /
33
- * keyboard-focus-order positioning exactly right without introducing
34
- * false positives is materially harder than the rest of Tier 1a. Being
35
- * lenient about condition (b) can only produce a false NEGATIVE (missing
36
- * a page whose only anchor link isn't a real skip mechanism, e.g. a
37
- * "back to top" link) — never a false positive — which matches this
38
- * engine's non-negotiable "fail is reserved for high-confidence
39
- * violations" policy. A future revision can tighten (b) once a
40
- * positional heuristic has been validated against real pages without
41
- * regressions.
42
- * - `fail` therefore means: no main landmark, no resolvable same-page
43
- * anchor link anywhere, and no heading anywhere on the page. That is a
44
- * strong, low-ambiguity signal that the page truly has zero recognized
45
- * bypass mechanism.
46
- */
47
-
48
- const id = 'bypass-blocks-present';
49
-
50
- const meta = {
51
- title: 'Page must provide a way to bypass repeated blocks',
52
- description:
53
- 'Checks that the page has at least one recognized WCAG 2.4.1 bypass-blocks mechanism: a main landmark, a working same-page anchor link, or a heading.',
54
- i18n: {
55
- titleKey: 'bypassBlocksPresent_title',
56
- descriptionKey: 'bypassBlocksPresent_description'
57
- },
58
- helpUrl: null,
59
- tags: ['wcag2a', 'wcag241', 'navigation', 'atomic', 'automatic'],
60
- wcagSc: ['2.4.1'],
61
- normativeMappings: [
62
- {
63
- standard: 'WCAG',
64
- version: '2.2',
65
- requirement: '2.4.1',
66
- title: 'Bypass Blocks',
67
- conformanceLevel: 'A'
68
- }
69
- ],
70
- defaultSeverity: 'serious',
71
- category: 'operable',
72
- type: 'automatic',
73
- defaultConfidence: 'medium',
74
- coverage: { facetsBySc: { '2.4.1': ['bypass-blocks-present'] } }
75
- };
76
-
77
- // This check is inherently whole-document (does the PAGE have this
78
- // property?), not evaluable per-subtree -- notApplicable when contextSelector
79
- // scoped this run narrower than the whole document, or when
80
- // engineOptions.fragment:true was set (see helpers.isWholeDocumentScope).
81
- function applicability(ctx) {
82
- return ctx.helpers.isWholeDocumentScope ? ctx.helpers.isWholeDocumentScope() : true;
83
- }
84
-
85
- function runInPage(ctx) {
86
- const { document, helpers, rule } = ctx;
87
-
88
- const body = document && document.body ? document.body : null;
89
- if (!body) {
90
- return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
91
- }
92
-
93
- const isAccTreeEligible =
94
- helpers && typeof helpers.isAccTreeEligible === 'function' ? helpers.isAccTreeEligible : null;
95
-
96
- function isExposedToAt(el) {
97
- if (!isAccTreeEligible) return true;
98
- try {
99
- const r = isAccTreeEligible(el, ctx);
100
- if (typeof r === 'boolean') return r;
101
- return !!(r && r.eligible);
102
- } catch {
103
- return true;
104
- }
105
- }
106
-
107
- function queryAll(selector) {
108
- try {
109
- return helpers && typeof helpers.queryAllSmart === 'function'
110
- ? helpers.queryAllSmart(selector)
111
- : document.querySelectorAll(selector);
112
- } catch {
113
- return [];
114
- }
115
- }
116
-
117
- // Filters through isAccTreeEligible (hidden/aria-hidden/display:none/inert
118
- // don't count as "a bypass mechanism is present") -- see page-has-heading-
119
- // one-manual.js's identical fix for the real-world trigger (CDC's flu
120
- // page, 2026-07-30: its only <h1> sits inside a display:none ancestor,
121
- // unreachable by sighted and screen reader users alike). A fully
122
- // non-rendered <main>/heading was previously credited here too, wrongly
123
- // returning `pass` for a page with zero actual bypass mechanisms.
124
- function hasMainLandmark() {
125
- for (const el of queryAll('main, [role="main"]')) {
126
- if (el && isExposedToAt(el)) return true;
127
- }
128
- return false;
129
- }
130
-
131
- function hasWorkingAnchorLink() {
132
- let links;
133
- try {
134
- links = document.querySelectorAll('a[href]');
135
- } catch {
136
- links = [];
137
- }
138
- for (const a of links) {
139
- if (!a || !a.getAttribute) continue;
140
- const href = String(a.getAttribute('href') || '').trim();
141
- if (href.length < 2 || href.charAt(0) !== '#') continue;
142
- let fragment = href.slice(1);
143
- try {
144
- fragment = decodeURIComponent(fragment);
145
- } catch {
146
- // keep raw fragment if it isn't a valid percent-encoded string
147
- }
148
- fragment = fragment.trim();
149
- if (!fragment) continue;
150
-
151
- let target;
152
- try {
153
- target = document.getElementById(fragment);
154
- } catch {
155
- target = null;
156
- }
157
- if (!target) {
158
- try {
159
- target = document.querySelector('a[name="' + fragment.replace(/"/g, '\\"') + '"]');
160
- } catch {
161
- target = null;
162
- }
163
- }
164
- if (target) return true;
165
- }
166
- return false;
167
- }
168
-
169
- function hasHeading() {
170
- for (const el of queryAll('h1, h2, h3, h4, h5, h6, [role="heading"]')) {
171
- if (el && isExposedToAt(el)) return true;
172
- }
173
- return false;
174
- }
175
-
176
- const mainLandmark = hasMainLandmark();
177
- const anchorLink = mainLandmark ? false : hasWorkingAnchorLink();
178
- const heading = mainLandmark || anchorLink ? false : hasHeading();
179
-
180
- if (mainLandmark || anchorLink || heading) {
181
- return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
182
- }
183
-
184
- const stableSelector = helpers.buildSelector ? helpers.buildSelector(body) : 'body';
185
- const html = helpers.getOuterHtmlSnippet
186
- ? helpers.getOuterHtmlSnippet(body)
187
- : (body.outerHTML || '').slice(0, 200);
188
-
189
- const occurrences = [
190
- {
191
- selector: stableSelector,
192
- html,
193
- summary: 'This page has no recognized way to bypass repeated blocks of content.',
194
- hint: 'Add a main landmark (<main> or role="main"), a working "skip to content" link, or heading elements that assistive technology can use to jump past repeated content.',
195
- i18n: {
196
- summaryKey: 'bypassBlocksPresent_summary_fail',
197
- hintKey: 'bypassBlocksPresent_hint_fail',
198
- params: {}
199
- },
200
- data: {
201
- details: { reasonCode: 'BYPASS_MECHANISM_ABSENT' },
202
- visibilityFilter: { targetSet: 'acc', accEligible: null, reasons: [] }
203
- }
204
- }
205
- ];
206
-
207
- return {
208
- ruleId: rule.ruleId,
209
- outcome: 'fail',
210
- severity: rule.defaultSeverity || 'serious',
211
- occurrences
212
- };
213
- }
214
-
215
- module.exports = { id, meta, runInPage, applicability };
@@ -1,19 +0,0 @@
1
- ruleId,type,title,tags,file
2
- area-alt-decorative,manual,<area> with alt=\,wcag2a wcag111 nontext images imagemap manual atomic,area-alt-decorative-manual.js
3
- area-alt-present,automatic,&lt;area&gt; must have an alt attribute,wcag2a wcag111 nontext images imagemap atomic automatic,area-alt-present.js
4
- area-alt-quality,manual,<area> alt text must be appropriate (manual review),wcag2a wcag111 nontext images imagemap manual atomic,area-alt-quality-manual.js
5
- canvas-text-alternative-present,automatic,<canvas> must provide a text alternative,wcag2a wcag111 nontext canvas atomic automatic,canvas-text-alternative-present.js
6
- canvas-text-alternative-quality,manual,<canvas> text alternative must be appropriate (manual review),wcag2a wcag111 nontext canvas manual atomic,canvas-text-alternative-quality-manual.js
7
- embed-text-alternative-present,automatic,<embed> must provide a text alternative,wcag2a wcag111 nontext embed atomic automatic,embed-text-alternative-present.js
8
- embed-text-alternative-quality,manual,<embed> text alternative must be appropriate (manual review),wcag2a wcag111 nontext embed manual atomic,embed-text-alternative-quality-manual.js
9
- img-alt-decorative,manual,<img> with alt=\,wcag2a wcag111 nontext images manual atomic,img-alt-decorative-manual.js
10
- img-alt-present,automatic,<img> must have an alt attribute,wcag2a wcag111 nontext images atomic automatic,img-alt-present.js
11
- img-alt-quality,manual,<img> alt text must be appropriate (manual review),wcag2a wcag111 nontext images manual atomic,img-alt-quality-manual.js
12
- input-image-alt-decorative,manual,<input type=\,wcag2a wcag111 forms images manual atomic,input-image-alt-decorative-manual.js
13
- input-image-alt-present,automatic,<input type=,wcag2a wcag111 nontext images atomic automatic,input-image-alt-present.js
14
- input-image-alt-quality,manual,<input type=\,wcag2a wcag111 forms images manual atomic,input-image-alt-quality-manual.js
15
- object-text-alternative-present,automatic,<object> must provide a text alternative,wcag2a wcag111 nontext object atomic automatic,object-text-alternative-present.js
16
- object-text-alternative-quality,manual,<object> text alternative must be appropriate (manual review),wcag2a wcag111 nontext object manual atomic,object-text-alternative-quality-manual.js
17
- svg-image-text-alternative-present,automatic,SVG <image> must have a text alternative,wcag2a wcag111 nontext svg image atomic automatic,svg-image-text-alternative-present.js
18
- svg-text-alternative-present,automatic,<svg> must provide a text alternative,wcag2a wcag111 svg nontext images atomic automatic,svg-text-alternative-present.js
19
- video-poster-text-alternative-present,automatic,<video> poster must have a text alternative,wcag2a wcag111 media video atomic automatic,video-poster-text-alternative-present.js