@surea11y/core 1.4.0 → 1.5.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 (115) hide show
  1. package/CHANGELOG.md +88 -7
  2. package/README.md +19 -3
  3. package/bin/surea11y-core.js +0 -0
  4. package/docs/API_STABILITY.md +2 -2
  5. package/docs/ARIA_DEPRECATION.md +95 -0
  6. package/docs/ENGINE_OPTIONS.md +14 -10
  7. package/docs/I18N.md +176 -20
  8. package/docs/INTEGRATION.md +28 -6
  9. package/docs/LIMITATIONS.md +1 -1
  10. package/docs/OUTPUT_SCHEMA.md +13 -3
  11. package/docs/REPORT.md +2 -0
  12. package/docs/RULE_AUTHORING.md +53 -0
  13. package/docs/RULE_CATALOG.md +6 -6
  14. package/docs/TROUBLESHOOTING.md +2 -2
  15. package/package.json +8 -1
  16. package/src/checks/automatic/aria-allowed-attr.js +661 -99
  17. package/src/checks/automatic/aria-allowed-role.js +14 -16
  18. package/src/checks/automatic/aria-braille-equivalent.js +17 -19
  19. package/src/checks/automatic/aria-conditional-attr.js +17 -19
  20. package/src/checks/automatic/aria-deprecated-role.js +122 -41
  21. package/src/checks/automatic/aria-hidden-body.js +2 -9
  22. package/src/checks/automatic/aria-hidden-focus.js +99 -18
  23. package/src/checks/automatic/aria-prohibited-attr.js +54 -55
  24. package/src/checks/automatic/aria-prohibited-children.js +26 -26
  25. package/src/checks/automatic/aria-required-attr.js +14 -17
  26. package/src/checks/automatic/aria-required-children.js +17 -20
  27. package/src/checks/automatic/aria-required-parent.js +17 -20
  28. package/src/checks/automatic/aria-roles-valid.js +66 -27
  29. package/src/checks/automatic/aria-valid-attr-value.js +18 -21
  30. package/src/checks/automatic/aria-valid-attr.js +14 -17
  31. package/src/checks/automatic/autocomplete-valid.js +52 -18
  32. package/src/checks/automatic/avoid-inline-spacing.js +192 -34
  33. package/src/checks/automatic/binary-control-name-present.js +30 -24
  34. package/src/checks/automatic/button-name-present.js +73 -45
  35. package/src/checks/automatic/canvas-text-alternative-present.js +15 -8
  36. package/src/checks/automatic/combobox-name-present.js +23 -18
  37. package/src/checks/automatic/css-orientation-lock.js +22 -22
  38. package/src/checks/automatic/definition-list-children-valid.js +18 -21
  39. package/src/checks/automatic/deprecated-elements-not-used.js +14 -16
  40. package/src/checks/automatic/dialog-name-present.js +24 -19
  41. package/src/checks/automatic/dlitem-parent-valid.js +15 -17
  42. package/src/checks/automatic/duplicate-id-aria.js +45 -37
  43. package/src/checks/automatic/form-control-programmatic-label-present.js +48 -4
  44. package/src/checks/automatic/form-control-single-label.js +109 -43
  45. package/src/checks/automatic/html-xml-lang-mismatch.js +2 -9
  46. package/src/checks/automatic/iframe-focusable-content.js +29 -31
  47. package/src/checks/automatic/iframe-name-present.js +15 -17
  48. package/src/checks/automatic/iframe-title-unique.js +18 -23
  49. package/src/checks/automatic/img-alt-present.js +16 -9
  50. package/src/checks/automatic/input-image-alt-present.js +99 -48
  51. package/src/checks/automatic/label-in-name.js +102 -33
  52. package/src/checks/automatic/language-page-present.js +5 -1
  53. package/src/checks/automatic/link-in-text-block.js +19 -21
  54. package/src/checks/automatic/link-name-present.js +75 -47
  55. package/src/checks/automatic/list-children-valid.js +15 -17
  56. package/src/checks/automatic/listbox-name-present.js +23 -18
  57. package/src/checks/automatic/listitem-parent-valid.js +14 -17
  58. package/src/checks/automatic/menuitem-name-present.js +24 -19
  59. package/src/checks/automatic/meta-refresh-no-exceptions.js +44 -18
  60. package/src/checks/automatic/meta-refresh-timing-absent.js +44 -21
  61. package/src/checks/automatic/meta-viewport-zoom-enabled.js +51 -32
  62. package/src/checks/automatic/meter-name-present.js +24 -19
  63. package/src/checks/automatic/nested-interactive-controls-absent.js +179 -51
  64. package/src/checks/automatic/object-text-alternative-present.js +14 -7
  65. package/src/checks/automatic/option-name-present.js +24 -19
  66. package/src/checks/automatic/progressbar-name-present.js +27 -22
  67. package/src/checks/automatic/searchbox-name-present.js +27 -18
  68. package/src/checks/automatic/server-side-image-map-absent.js +15 -18
  69. package/src/checks/automatic/slider-name-present.js +26 -19
  70. package/src/checks/automatic/spinbutton-name-present.js +27 -18
  71. package/src/checks/automatic/summary-name-present.js +24 -19
  72. package/src/checks/automatic/tab-name-present.js +24 -19
  73. package/src/checks/automatic/table-headers-attr-valid.js +15 -17
  74. package/src/checks/automatic/table-th-has-data-cells.js +82 -25
  75. package/src/checks/automatic/target-size-minimum.js +104 -81
  76. package/src/checks/automatic/td-has-header.js +15 -20
  77. package/src/checks/automatic/textbox-name-present.js +23 -18
  78. package/src/checks/automatic/tooltip-name-present.js +24 -19
  79. package/src/checks/automatic/treeitem-name-present.js +24 -19
  80. package/src/checks/automatic/valid-lang.js +31 -20
  81. package/src/checks/manual/accesskeys-manual.js +18 -19
  82. package/src/checks/manual/aria-checked-state-mismatch-manual.js +19 -22
  83. package/src/checks/{automatic/bypass-blocks-present.js → manual/bypass-blocks-present-manual.js} +98 -44
  84. package/src/checks/manual/empty-heading-manual.js +15 -17
  85. package/src/checks/manual/empty-table-header-manual.js +27 -30
  86. package/src/checks/manual/form-control-programmatic-label-quality-manual.js +2 -9
  87. package/src/checks/manual/heading-order-manual.js +17 -22
  88. package/src/checks/manual/image-redundant-alt-manual.js +14 -17
  89. package/src/checks/manual/input-image-alt-decorative-manual.js +24 -0
  90. package/src/checks/manual/label-title-only-manual.js +15 -17
  91. package/src/checks/manual/landmark-banner-is-top-level-manual.js +25 -39
  92. package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +21 -27
  93. package/src/checks/manual/landmark-main-is-top-level-manual.js +14 -17
  94. package/src/checks/manual/landmark-no-duplicate-banner-manual.js +2 -7
  95. package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +2 -7
  96. package/src/checks/manual/landmark-no-duplicate-main-manual.js +2 -7
  97. package/src/checks/manual/landmark-one-main-manual.js +2 -9
  98. package/src/checks/manual/landmark-unique-manual.js +22 -27
  99. package/src/checks/manual/link-name-quality-manual.js +15 -17
  100. package/src/checks/manual/meta-viewport-large-manual.js +14 -17
  101. package/src/checks/manual/mouse-only-event-handlers-manual.js +17 -19
  102. package/src/checks/manual/page-has-heading-one-manual.js +2 -9
  103. package/src/checks/manual/presentation-role-conflict-manual.js +19 -21
  104. package/src/checks/manual/region-manual.js +13 -6
  105. package/src/checks/manual/scope-attr-valid-manual.js +14 -17
  106. package/src/checks/manual/skip-link-manual.js +39 -47
  107. package/src/checks/manual/tabindex-manual.js +14 -17
  108. package/src/checks/manual/table-duplicate-name-manual.js +14 -17
  109. package/src/core.js +8818 -4330
  110. package/src/report.js +14 -0
  111. package/src/sarif.js +2 -2
  112. package/surea11y.browser.js +4023 -3911
  113. package/surea11y.i18n.de.js +22 -0
  114. package/surea11y.i18n.es.js +22 -0
  115. package/surea11y.i18n.fr.js +22 -0
@@ -16,6 +16,9 @@
16
16
  * supported on any element (the "global" ARIA states/properties, e.g.
17
17
  * aria-label/aria-hidden/aria-describedby), or (b) explicitly listed as
18
18
  * a required or supported state/property for the element's role.
19
+ * An attribute ARIA deprecated (rather than prohibited) on the role is
20
+ * still allowed: it is reported as CANTTELL (see helpers.aria.isDeprecatedAttr)
21
+ * so the author decides, not as a not-allowed FAIL.
19
22
  * @implementation-notes
20
23
  * - Only evaluated for elements with an explicit role, since the global
21
24
  * set already covers implicit-role elements without asserting anything
@@ -81,6 +84,7 @@ function runInPage(ctx) {
81
84
  // Declared inside runInPage (rather than at module scope) because the
82
85
  // build inlines only this function's own source text — see
83
86
  // scripts/build-core.js header ("runInPage MUST be self-contained").
87
+ // <generated:aria-global-attrs>
84
88
  const GLOBAL_ATTRS = [
85
89
  'aria-atomic',
86
90
  'aria-braillelabel',
@@ -91,14 +95,10 @@ function runInPage(ctx) {
91
95
  'aria-describedby',
92
96
  'aria-description',
93
97
  'aria-details',
94
- 'aria-disabled',
95
98
  'aria-dropeffect',
96
- 'aria-errormessage',
97
99
  'aria-flowto',
98
100
  'aria-grabbed',
99
- 'aria-haspopup',
100
101
  'aria-hidden',
101
- 'aria-invalid',
102
102
  'aria-keyshortcuts',
103
103
  'aria-label',
104
104
  'aria-labelledby',
@@ -107,194 +107,736 @@ function runInPage(ctx) {
107
107
  'aria-relevant',
108
108
  'aria-roledescription'
109
109
  ];
110
+ // </generated:aria-global-attrs>
110
111
 
111
112
  // Per-role supported (non-global) states/properties. Deliberately
112
113
  // conservative — see src/core/aria-helpers.js file header for the same
113
114
  // confidence-scoping rationale; only well-established, unambiguous
114
115
  // role/attribute pairings from the WAI-ARIA role definitions are listed.
116
+ // <generated:aria-implicit-roles>
117
+ const IMPLICIT_ROLE_BY_ELEMENT = {
118
+ article: 'article',
119
+ blockquote: 'blockquote',
120
+ button: 'button',
121
+ caption: 'caption',
122
+ code: 'code',
123
+ dd: 'definition',
124
+ del: 'deletion',
125
+ details: 'group',
126
+ dfn: 'term',
127
+ dialog: 'dialog',
128
+ dt: 'term',
129
+ em: 'emphasis',
130
+ fieldset: 'group',
131
+ figure: 'figure',
132
+ h1: 'heading',
133
+ h2: 'heading',
134
+ h3: 'heading',
135
+ h4: 'heading',
136
+ h5: 'heading',
137
+ h6: 'heading',
138
+ hr: 'separator',
139
+ ins: 'insertion',
140
+ main: 'main',
141
+ mark: 'mark',
142
+ menu: 'list',
143
+ meter: 'meter',
144
+ nav: 'navigation',
145
+ ol: 'list',
146
+ optgroup: 'group',
147
+ option: 'option',
148
+ output: 'status',
149
+ p: 'paragraph',
150
+ progress: 'progressbar',
151
+ strong: 'strong',
152
+ sub: 'subscript',
153
+ sup: 'superscript',
154
+ textarea: 'textbox',
155
+ time: 'time',
156
+ ul: 'list',
157
+ 'input[type=text]': 'textbox',
158
+ 'input[type=tel]': 'textbox',
159
+ 'input[type=url]': 'textbox',
160
+ 'input[type=email]': 'textbox',
161
+ 'input[type=password]': 'textbox',
162
+ 'input[type=search]': 'searchbox',
163
+ 'input[type=number]': 'spinbutton',
164
+ 'input[type=range]': 'slider',
165
+ 'input[type=checkbox]': 'checkbox',
166
+ 'input[type=radio]': 'radio',
167
+ 'input[type=button]': 'button',
168
+ 'input[type=submit]': 'button',
169
+ 'input[type=reset]': 'button',
170
+ 'input[type=image]': 'button'
171
+ };
172
+ const NON_GLOBAL_ARIA_ATTR_SELECTOR =
173
+ '[aria-activedescendant], [aria-autocomplete], [aria-checked], [aria-colcount], [aria-colindex], [aria-colspan], [aria-disabled], [aria-errormessage], [aria-expanded], [aria-haspopup], [aria-invalid], [aria-level], [aria-modal], [aria-multiline], [aria-multiselectable], [aria-orientation], [aria-placeholder], [aria-posinset], [aria-pressed], [aria-readonly], [aria-required], [aria-rowcount], [aria-rowindex], [aria-rowspan], [aria-selected], [aria-setsize], [aria-sort], [aria-valuemax], [aria-valuemin], [aria-valuenow], [aria-valuetext]';
174
+ // </generated:aria-implicit-roles>
175
+
176
+ // <generated:aria-role-attrs>
115
177
  const SUPPORTED_ATTRS_BY_ROLE = {
178
+ alert: [],
116
179
  alertdialog: ['aria-modal'],
117
- checkbox: ['aria-checked', 'aria-readonly', 'aria-required', 'aria-expanded'],
180
+ application: [
181
+ 'aria-activedescendant',
182
+ 'aria-disabled',
183
+ 'aria-errormessage',
184
+ 'aria-expanded',
185
+ 'aria-haspopup',
186
+ 'aria-invalid'
187
+ ],
188
+ article: ['aria-posinset', 'aria-setsize'],
189
+ banner: [],
190
+ blockquote: [],
191
+ button: ['aria-disabled', 'aria-expanded', 'aria-haspopup', 'aria-pressed'],
192
+ caption: [],
193
+ cell: ['aria-colindex', 'aria-colspan', 'aria-rowindex', 'aria-rowspan'],
194
+ checkbox: [
195
+ 'aria-checked',
196
+ 'aria-disabled',
197
+ 'aria-errormessage',
198
+ 'aria-expanded',
199
+ 'aria-invalid',
200
+ 'aria-readonly',
201
+ 'aria-required'
202
+ ],
203
+ code: [],
118
204
  columnheader: [
119
- 'aria-sort',
120
205
  'aria-colindex',
121
206
  'aria-colspan',
207
+ 'aria-disabled',
208
+ 'aria-errormessage',
209
+ 'aria-expanded',
210
+ 'aria-haspopup',
211
+ 'aria-invalid',
122
212
  'aria-readonly',
123
213
  'aria-required',
124
214
  'aria-rowindex',
125
215
  'aria-rowspan',
126
216
  'aria-selected',
127
- 'aria-expanded'
217
+ 'aria-sort'
128
218
  ],
129
219
  combobox: [
130
- 'aria-expanded',
220
+ 'aria-activedescendant',
131
221
  'aria-autocomplete',
222
+ 'aria-disabled',
223
+ 'aria-errormessage',
224
+ 'aria-expanded',
225
+ 'aria-haspopup',
226
+ 'aria-invalid',
132
227
  'aria-readonly',
133
- 'aria-required',
134
- 'aria-activedescendant'
228
+ 'aria-required'
135
229
  ],
230
+ complementary: [],
231
+ contentinfo: [],
232
+ definition: [],
233
+ deletion: [],
136
234
  dialog: ['aria-modal'],
235
+ directory: [],
236
+ 'doc-abstract': [
237
+ 'aria-disabled',
238
+ 'aria-errormessage',
239
+ 'aria-expanded',
240
+ 'aria-haspopup',
241
+ 'aria-invalid'
242
+ ],
243
+ 'doc-acknowledgments': [
244
+ 'aria-disabled',
245
+ 'aria-errormessage',
246
+ 'aria-expanded',
247
+ 'aria-haspopup',
248
+ 'aria-invalid'
249
+ ],
250
+ 'doc-afterword': [
251
+ 'aria-disabled',
252
+ 'aria-errormessage',
253
+ 'aria-expanded',
254
+ 'aria-haspopup',
255
+ 'aria-invalid'
256
+ ],
257
+ 'doc-appendix': [
258
+ 'aria-disabled',
259
+ 'aria-errormessage',
260
+ 'aria-expanded',
261
+ 'aria-haspopup',
262
+ 'aria-invalid'
263
+ ],
264
+ 'doc-backlink': [
265
+ 'aria-disabled',
266
+ 'aria-errormessage',
267
+ 'aria-expanded',
268
+ 'aria-haspopup',
269
+ 'aria-invalid'
270
+ ],
271
+ 'doc-biblioentry': [
272
+ 'aria-disabled',
273
+ 'aria-errormessage',
274
+ 'aria-expanded',
275
+ 'aria-haspopup',
276
+ 'aria-invalid',
277
+ 'aria-level',
278
+ 'aria-posinset',
279
+ 'aria-setsize'
280
+ ],
281
+ 'doc-bibliography': [
282
+ 'aria-disabled',
283
+ 'aria-errormessage',
284
+ 'aria-expanded',
285
+ 'aria-haspopup',
286
+ 'aria-invalid'
287
+ ],
288
+ 'doc-biblioref': [
289
+ 'aria-disabled',
290
+ 'aria-errormessage',
291
+ 'aria-expanded',
292
+ 'aria-haspopup',
293
+ 'aria-invalid'
294
+ ],
295
+ 'doc-chapter': [
296
+ 'aria-disabled',
297
+ 'aria-errormessage',
298
+ 'aria-expanded',
299
+ 'aria-haspopup',
300
+ 'aria-invalid'
301
+ ],
302
+ 'doc-colophon': [
303
+ 'aria-disabled',
304
+ 'aria-errormessage',
305
+ 'aria-expanded',
306
+ 'aria-haspopup',
307
+ 'aria-invalid'
308
+ ],
309
+ 'doc-conclusion': [
310
+ 'aria-disabled',
311
+ 'aria-errormessage',
312
+ 'aria-expanded',
313
+ 'aria-haspopup',
314
+ 'aria-invalid'
315
+ ],
316
+ 'doc-cover': [
317
+ 'aria-disabled',
318
+ 'aria-errormessage',
319
+ 'aria-expanded',
320
+ 'aria-haspopup',
321
+ 'aria-invalid'
322
+ ],
323
+ 'doc-credit': [
324
+ 'aria-disabled',
325
+ 'aria-errormessage',
326
+ 'aria-expanded',
327
+ 'aria-haspopup',
328
+ 'aria-invalid'
329
+ ],
330
+ 'doc-credits': [
331
+ 'aria-disabled',
332
+ 'aria-errormessage',
333
+ 'aria-expanded',
334
+ 'aria-haspopup',
335
+ 'aria-invalid'
336
+ ],
337
+ 'doc-dedication': [
338
+ 'aria-disabled',
339
+ 'aria-errormessage',
340
+ 'aria-expanded',
341
+ 'aria-haspopup',
342
+ 'aria-invalid'
343
+ ],
344
+ 'doc-endnote': [
345
+ 'aria-disabled',
346
+ 'aria-errormessage',
347
+ 'aria-expanded',
348
+ 'aria-haspopup',
349
+ 'aria-invalid',
350
+ 'aria-level',
351
+ 'aria-posinset',
352
+ 'aria-setsize'
353
+ ],
354
+ 'doc-endnotes': [
355
+ 'aria-disabled',
356
+ 'aria-errormessage',
357
+ 'aria-expanded',
358
+ 'aria-haspopup',
359
+ 'aria-invalid'
360
+ ],
361
+ 'doc-epigraph': [
362
+ 'aria-disabled',
363
+ 'aria-errormessage',
364
+ 'aria-expanded',
365
+ 'aria-haspopup',
366
+ 'aria-invalid'
367
+ ],
368
+ 'doc-epilogue': [
369
+ 'aria-disabled',
370
+ 'aria-errormessage',
371
+ 'aria-expanded',
372
+ 'aria-haspopup',
373
+ 'aria-invalid'
374
+ ],
375
+ 'doc-errata': [
376
+ 'aria-disabled',
377
+ 'aria-errormessage',
378
+ 'aria-expanded',
379
+ 'aria-haspopup',
380
+ 'aria-invalid'
381
+ ],
382
+ 'doc-example': [
383
+ 'aria-disabled',
384
+ 'aria-errormessage',
385
+ 'aria-expanded',
386
+ 'aria-haspopup',
387
+ 'aria-invalid'
388
+ ],
389
+ 'doc-footnote': [
390
+ 'aria-disabled',
391
+ 'aria-errormessage',
392
+ 'aria-expanded',
393
+ 'aria-haspopup',
394
+ 'aria-invalid'
395
+ ],
396
+ 'doc-foreword': [
397
+ 'aria-disabled',
398
+ 'aria-errormessage',
399
+ 'aria-expanded',
400
+ 'aria-haspopup',
401
+ 'aria-invalid'
402
+ ],
403
+ 'doc-glossary': [
404
+ 'aria-disabled',
405
+ 'aria-errormessage',
406
+ 'aria-expanded',
407
+ 'aria-haspopup',
408
+ 'aria-invalid'
409
+ ],
410
+ 'doc-glossref': [
411
+ 'aria-disabled',
412
+ 'aria-errormessage',
413
+ 'aria-expanded',
414
+ 'aria-haspopup',
415
+ 'aria-invalid'
416
+ ],
417
+ 'doc-index': [
418
+ 'aria-disabled',
419
+ 'aria-errormessage',
420
+ 'aria-expanded',
421
+ 'aria-haspopup',
422
+ 'aria-invalid'
423
+ ],
424
+ 'doc-introduction': [
425
+ 'aria-disabled',
426
+ 'aria-errormessage',
427
+ 'aria-expanded',
428
+ 'aria-haspopup',
429
+ 'aria-invalid'
430
+ ],
431
+ 'doc-noteref': [
432
+ 'aria-disabled',
433
+ 'aria-errormessage',
434
+ 'aria-expanded',
435
+ 'aria-haspopup',
436
+ 'aria-invalid'
437
+ ],
438
+ 'doc-notice': [
439
+ 'aria-disabled',
440
+ 'aria-errormessage',
441
+ 'aria-expanded',
442
+ 'aria-haspopup',
443
+ 'aria-invalid'
444
+ ],
445
+ 'doc-pagebreak': [
446
+ 'aria-disabled',
447
+ 'aria-errormessage',
448
+ 'aria-expanded',
449
+ 'aria-haspopup',
450
+ 'aria-invalid',
451
+ 'aria-orientation',
452
+ 'aria-valuemax',
453
+ 'aria-valuemin',
454
+ 'aria-valuenow',
455
+ 'aria-valuetext'
456
+ ],
457
+ 'doc-pagefooter': ['aria-disabled', 'aria-errormessage', 'aria-haspopup', 'aria-invalid'],
458
+ 'doc-pageheader': ['aria-disabled', 'aria-errormessage', 'aria-haspopup', 'aria-invalid'],
459
+ 'doc-pagelist': [
460
+ 'aria-disabled',
461
+ 'aria-errormessage',
462
+ 'aria-expanded',
463
+ 'aria-haspopup',
464
+ 'aria-invalid'
465
+ ],
466
+ 'doc-part': [
467
+ 'aria-disabled',
468
+ 'aria-errormessage',
469
+ 'aria-expanded',
470
+ 'aria-haspopup',
471
+ 'aria-invalid'
472
+ ],
473
+ 'doc-preface': [
474
+ 'aria-disabled',
475
+ 'aria-errormessage',
476
+ 'aria-expanded',
477
+ 'aria-haspopup',
478
+ 'aria-invalid'
479
+ ],
480
+ 'doc-prologue': [
481
+ 'aria-disabled',
482
+ 'aria-errormessage',
483
+ 'aria-expanded',
484
+ 'aria-haspopup',
485
+ 'aria-invalid'
486
+ ],
487
+ 'doc-pullquote': [],
488
+ 'doc-qna': [
489
+ 'aria-disabled',
490
+ 'aria-errormessage',
491
+ 'aria-expanded',
492
+ 'aria-haspopup',
493
+ 'aria-invalid'
494
+ ],
495
+ 'doc-subtitle': [
496
+ 'aria-disabled',
497
+ 'aria-errormessage',
498
+ 'aria-expanded',
499
+ 'aria-haspopup',
500
+ 'aria-invalid'
501
+ ],
502
+ 'doc-tip': [
503
+ 'aria-disabled',
504
+ 'aria-errormessage',
505
+ 'aria-expanded',
506
+ 'aria-haspopup',
507
+ 'aria-invalid'
508
+ ],
509
+ 'doc-toc': [
510
+ 'aria-disabled',
511
+ 'aria-errormessage',
512
+ 'aria-expanded',
513
+ 'aria-haspopup',
514
+ 'aria-invalid'
515
+ ],
516
+ document: [],
517
+ emphasis: [],
518
+ feed: [],
519
+ figure: [],
520
+ form: [],
521
+ generic: [],
522
+ 'graphics-document': [
523
+ 'aria-disabled',
524
+ 'aria-errormessage',
525
+ 'aria-expanded',
526
+ 'aria-haspopup',
527
+ 'aria-invalid'
528
+ ],
529
+ 'graphics-object': [
530
+ 'aria-activedescendant',
531
+ 'aria-disabled',
532
+ 'aria-errormessage',
533
+ 'aria-expanded',
534
+ 'aria-haspopup',
535
+ 'aria-invalid'
536
+ ],
537
+ 'graphics-symbol': [
538
+ 'aria-disabled',
539
+ 'aria-errormessage',
540
+ 'aria-expanded',
541
+ 'aria-haspopup',
542
+ 'aria-invalid'
543
+ ],
137
544
  grid: [
545
+ 'aria-activedescendant',
546
+ 'aria-colcount',
547
+ 'aria-disabled',
138
548
  'aria-multiselectable',
139
549
  'aria-readonly',
140
- 'aria-colcount',
141
- 'aria-rowcount',
142
- 'aria-activedescendant'
550
+ 'aria-rowcount'
143
551
  ],
144
552
  gridcell: [
145
- 'aria-selected',
146
- 'aria-readonly',
147
- 'aria-required',
148
553
  'aria-colindex',
149
554
  'aria-colspan',
555
+ 'aria-disabled',
556
+ 'aria-errormessage',
557
+ 'aria-expanded',
558
+ 'aria-haspopup',
559
+ 'aria-invalid',
560
+ 'aria-readonly',
561
+ 'aria-required',
150
562
  'aria-rowindex',
151
563
  'aria-rowspan',
152
- 'aria-expanded'
564
+ 'aria-selected'
153
565
  ],
566
+ group: ['aria-activedescendant', 'aria-disabled'],
154
567
  heading: ['aria-level'],
568
+ img: [],
569
+ insertion: [],
570
+ link: ['aria-disabled', 'aria-expanded', 'aria-haspopup'],
571
+ list: [],
155
572
  listbox: [
573
+ 'aria-activedescendant',
574
+ 'aria-disabled',
575
+ 'aria-errormessage',
576
+ 'aria-expanded',
577
+ 'aria-invalid',
156
578
  'aria-multiselectable',
157
- 'aria-readonly',
158
- 'aria-required',
159
579
  'aria-orientation',
160
- 'aria-expanded',
161
- 'aria-activedescendant'
580
+ 'aria-readonly',
581
+ 'aria-required'
162
582
  ],
163
583
  listitem: ['aria-level', 'aria-posinset', 'aria-setsize'],
164
- menu: ['aria-activedescendant', 'aria-orientation'],
165
- menubar: ['aria-activedescendant', 'aria-orientation'],
584
+ log: [],
585
+ main: [],
586
+ mark: [],
587
+ marquee: [],
588
+ math: [],
589
+ menu: ['aria-activedescendant', 'aria-disabled', 'aria-orientation'],
590
+ menubar: ['aria-activedescendant', 'aria-disabled', 'aria-orientation'],
591
+ menuitem: ['aria-disabled', 'aria-expanded', 'aria-haspopup', 'aria-posinset', 'aria-setsize'],
166
592
  menuitemcheckbox: [
167
593
  'aria-checked',
594
+ 'aria-disabled',
595
+ 'aria-errormessage',
168
596
  'aria-expanded',
597
+ 'aria-haspopup',
598
+ 'aria-invalid',
599
+ 'aria-posinset',
169
600
  'aria-readonly',
170
601
  'aria-required',
171
- 'aria-posinset',
172
602
  'aria-setsize'
173
603
  ],
174
604
  menuitemradio: [
175
605
  'aria-checked',
606
+ 'aria-disabled',
607
+ 'aria-errormessage',
176
608
  'aria-expanded',
609
+ 'aria-haspopup',
610
+ 'aria-invalid',
611
+ 'aria-posinset',
177
612
  'aria-readonly',
178
613
  'aria-required',
179
- 'aria-posinset',
180
614
  'aria-setsize'
181
615
  ],
182
- meter: ['aria-valuenow', 'aria-valuemin', 'aria-valuemax', 'aria-valuetext'],
183
- option: ['aria-selected', 'aria-checked', 'aria-posinset', 'aria-setsize'],
184
- progressbar: ['aria-valuenow', 'aria-valuemin', 'aria-valuemax', 'aria-valuetext'],
185
- radio: ['aria-checked', 'aria-posinset', 'aria-setsize'],
186
- radiogroup: ['aria-readonly', 'aria-required', 'aria-orientation', 'aria-activedescendant'],
616
+ meter: ['aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext'],
617
+ navigation: [],
618
+ none: [],
619
+ note: [],
620
+ option: ['aria-checked', 'aria-disabled', 'aria-posinset', 'aria-selected', 'aria-setsize'],
621
+ paragraph: [],
622
+ presentation: [],
623
+ progressbar: ['aria-valuemax', 'aria-valuemin', 'aria-valuenow', 'aria-valuetext'],
624
+ radio: ['aria-checked', 'aria-disabled', 'aria-posinset', 'aria-setsize'],
625
+ radiogroup: [
626
+ 'aria-activedescendant',
627
+ 'aria-disabled',
628
+ 'aria-errormessage',
629
+ 'aria-invalid',
630
+ 'aria-orientation',
631
+ 'aria-readonly',
632
+ 'aria-required'
633
+ ],
634
+ region: [],
187
635
  row: [
188
- 'aria-selected',
636
+ 'aria-activedescendant',
637
+ 'aria-colindex',
638
+ 'aria-disabled',
639
+ 'aria-expanded',
189
640
  'aria-level',
190
641
  'aria-posinset',
191
- 'aria-setsize',
192
- 'aria-colindex',
193
642
  'aria-rowindex',
194
- 'aria-expanded',
195
- 'aria-activedescendant'
643
+ 'aria-selected',
644
+ 'aria-setsize'
196
645
  ],
646
+ rowgroup: [],
197
647
  rowheader: [
198
- 'aria-sort',
199
648
  'aria-colindex',
200
649
  'aria-colspan',
650
+ 'aria-disabled',
651
+ 'aria-errormessage',
652
+ 'aria-expanded',
653
+ 'aria-haspopup',
654
+ 'aria-invalid',
201
655
  'aria-readonly',
202
656
  'aria-required',
203
657
  'aria-rowindex',
204
658
  'aria-rowspan',
205
659
  'aria-selected',
206
- 'aria-expanded'
660
+ 'aria-sort'
207
661
  ],
208
662
  scrollbar: [
209
- 'aria-valuenow',
210
- 'aria-valuemin',
211
- 'aria-valuemax',
212
- 'aria-valuetext',
663
+ 'aria-disabled',
213
664
  'aria-orientation',
214
- 'aria-controls'
665
+ 'aria-valuemax',
666
+ 'aria-valuemin',
667
+ 'aria-valuenow',
668
+ 'aria-valuetext'
215
669
  ],
670
+ search: [],
216
671
  searchbox: [
217
672
  'aria-activedescendant',
218
673
  'aria-autocomplete',
674
+ 'aria-disabled',
675
+ 'aria-errormessage',
676
+ 'aria-haspopup',
677
+ 'aria-invalid',
219
678
  'aria-multiline',
220
679
  'aria-placeholder',
221
680
  'aria-readonly',
222
681
  'aria-required'
223
682
  ],
224
683
  separator: [
225
- 'aria-valuenow',
226
- 'aria-valuemin',
684
+ 'aria-disabled',
685
+ 'aria-orientation',
227
686
  'aria-valuemax',
228
- 'aria-valuetext',
229
- 'aria-orientation'
687
+ 'aria-valuemin',
688
+ 'aria-valuenow',
689
+ 'aria-valuetext'
230
690
  ],
231
691
  slider: [
232
- 'aria-valuenow',
233
- 'aria-valuemin',
234
- 'aria-valuemax',
235
- 'aria-valuetext',
692
+ 'aria-disabled',
693
+ 'aria-errormessage',
694
+ 'aria-haspopup',
695
+ 'aria-invalid',
236
696
  'aria-orientation',
237
- 'aria-readonly'
697
+ 'aria-readonly',
698
+ 'aria-valuemax',
699
+ 'aria-valuemin',
700
+ 'aria-valuenow',
701
+ 'aria-valuetext'
238
702
  ],
239
703
  spinbutton: [
240
- 'aria-valuenow',
241
- 'aria-valuemin',
242
- 'aria-valuemax',
243
- 'aria-valuetext',
704
+ 'aria-activedescendant',
705
+ 'aria-disabled',
706
+ 'aria-errormessage',
707
+ 'aria-invalid',
244
708
  'aria-readonly',
245
709
  'aria-required',
246
- 'aria-activedescendant'
710
+ 'aria-valuemax',
711
+ 'aria-valuemin',
712
+ 'aria-valuenow',
713
+ 'aria-valuetext'
714
+ ],
715
+ status: [],
716
+ strong: [],
717
+ subscript: [],
718
+ superscript: [],
719
+ switch: [
720
+ 'aria-checked',
721
+ 'aria-disabled',
722
+ 'aria-errormessage',
723
+ 'aria-expanded',
724
+ 'aria-invalid',
725
+ 'aria-readonly',
726
+ 'aria-required'
727
+ ],
728
+ tab: [
729
+ 'aria-disabled',
730
+ 'aria-expanded',
731
+ 'aria-haspopup',
732
+ 'aria-posinset',
733
+ 'aria-selected',
734
+ 'aria-setsize'
247
735
  ],
248
- switch: ['aria-checked', 'aria-expanded', 'aria-readonly', 'aria-required'],
249
- tab: ['aria-selected', 'aria-expanded', 'aria-posinset', 'aria-setsize'],
250
736
  table: ['aria-colcount', 'aria-rowcount'],
251
- tablist: ['aria-multiselectable', 'aria-orientation', 'aria-level', 'aria-activedescendant'],
737
+ tablist: [
738
+ 'aria-activedescendant',
739
+ 'aria-disabled',
740
+ 'aria-level',
741
+ 'aria-multiselectable',
742
+ 'aria-orientation'
743
+ ],
744
+ tabpanel: [],
745
+ term: [],
252
746
  textbox: [
253
747
  'aria-activedescendant',
254
748
  'aria-autocomplete',
749
+ 'aria-disabled',
750
+ 'aria-errormessage',
751
+ 'aria-haspopup',
752
+ 'aria-invalid',
255
753
  'aria-multiline',
256
754
  'aria-placeholder',
257
755
  'aria-readonly',
258
756
  'aria-required'
259
757
  ],
260
- toolbar: ['aria-activedescendant', 'aria-orientation'],
261
- tree: ['aria-multiselectable', 'aria-required', 'aria-orientation', 'aria-activedescendant'],
758
+ time: [],
759
+ timer: [],
760
+ toolbar: ['aria-activedescendant', 'aria-disabled', 'aria-orientation'],
761
+ tooltip: [],
762
+ tree: [
763
+ 'aria-activedescendant',
764
+ 'aria-disabled',
765
+ 'aria-errormessage',
766
+ 'aria-invalid',
767
+ 'aria-multiselectable',
768
+ 'aria-orientation',
769
+ 'aria-required'
770
+ ],
262
771
  treegrid: [
772
+ 'aria-activedescendant',
773
+ 'aria-colcount',
774
+ 'aria-disabled',
775
+ 'aria-errormessage',
776
+ 'aria-invalid',
263
777
  'aria-multiselectable',
778
+ 'aria-orientation',
264
779
  'aria-readonly',
265
780
  'aria-required',
266
- 'aria-orientation',
267
- 'aria-colcount',
268
- 'aria-rowcount',
269
- 'aria-activedescendant'
781
+ 'aria-rowcount'
270
782
  ],
271
783
  treeitem: [
272
784
  'aria-checked',
273
- 'aria-selected',
785
+ 'aria-disabled',
274
786
  'aria-expanded',
787
+ 'aria-haspopup',
275
788
  'aria-level',
276
789
  'aria-posinset',
790
+ 'aria-selected',
277
791
  'aria-setsize'
278
792
  ]
279
793
  };
794
+ // </generated:aria-role-attrs>
280
795
 
281
796
  const globalSet = new Set(GLOBAL_ATTRS);
797
+ // [role] keeps the explicit-role path; the attribute selector brings in
798
+ // elements judged by their implicit role. Only non-global attributes can be
799
+ // disallowed, so nothing else needs visiting.
800
+ const selector = '[role], ' + NON_GLOBAL_ARIA_ATTR_SELECTOR;
282
801
  const nodes = helpers.queryAllSmart
283
- ? helpers.queryAllSmart('[role]')
284
- : helpers.queryAll('[role]');
802
+ ? helpers.queryAllSmart(selector)
803
+ : helpers.queryAll(selector);
285
804
 
286
- const occurrences = [];
805
+ const failOccurrences = [];
806
+ const cantTellOccurrences = [];
287
807
  let applicableCount = 0;
288
808
 
289
809
  for (const el of nodes) {
290
810
  if (!el || !el.attributes) continue;
291
811
 
292
- const role = ariaHelpers.getExplicitRole(el);
812
+ // ACT 5c01ea scopes the rule to any element carrying an ARIA attribute, so
813
+ // an element with no role attribute is judged against its implicit role.
814
+ // Only elements whose implicit role is context-free are covered; the
815
+ // generator lists what is excluded and why.
816
+ const explicitRole = ariaHelpers.getExplicitRole(el);
817
+ let role = explicitRole;
818
+ if (!role) {
819
+ const tag = String(el.tagName || '').toLowerCase();
820
+ const key =
821
+ tag === 'input'
822
+ ? 'input[type=' + String(el.getAttribute('type') || 'text').toLowerCase() + ']'
823
+ : tag;
824
+ role = Object.prototype.hasOwnProperty.call(IMPLICIT_ROLE_BY_ELEMENT, key)
825
+ ? IMPLICIT_ROLE_BY_ELEMENT[key]
826
+ : '';
827
+ }
293
828
  if (!role || !ariaHelpers.isValidConcreteRole(role)) continue; // aria-roles-valid's concern
294
829
 
295
- // Deliberately scoped: only roles with an explicit supported-attrs
296
- // entry are evaluated (see file header — kept narrow to avoid
297
- // over-claiming constraints for roles not yet modeled here).
830
+ // Presentational role conflict resolution drops role="none"/"presentation"
831
+ // when the element is focusable or carries global ARIA, so the implicit
832
+ // role decides which attributes are supported. Judging against the
833
+ // presentational role would flag valid markup such as
834
+ // <button role="none" aria-pressed="false">; presentation-role-conflict
835
+ // owns this case.
836
+ if (role === 'none' || role === 'presentation') continue;
837
+
838
+ // Roles absent from the generated table are unknown to ARIA, so there is
839
+ // nothing to judge them against.
298
840
  const roleSupported = SUPPORTED_ATTRS_BY_ROLE[role];
299
841
  if (!roleSupported) continue;
300
842
  const roleSupportedSet = new Set(roleSupported);
@@ -316,39 +858,59 @@ function runInPage(ctx) {
316
858
 
317
859
  if (!disallowed || !disallowed.length) continue;
318
860
 
319
- const stableSelector = helpers.buildSelector ? helpers.buildSelector(el) : 'html';
320
- const html = helpers.getOuterHtmlSnippet ? helpers.getOuterHtmlSnippet(el) : el.outerHTML || '';
321
-
322
861
  for (const name of disallowed) {
323
- occurrences.push({
324
- selector: stableSelector,
325
- html,
326
- summary: 'This ARIA attribute is not permitted for this element’s role.',
327
- hint: 'Remove this attribute, or use a role that supports it.',
328
- i18n: {
329
- summaryKey: 'ariaAllowedAttr_summary_fail',
330
- hintKey: 'ariaAllowedAttr_hint_fail',
331
- params: { attr: name, role }
332
- },
333
- data: {
334
- details: { reasonCode: 'ARIA_ATTR_NOT_ALLOWED', attr: name, role }
335
- }
336
- });
862
+ // A property ARIA deprecated (rather than prohibited) on this role is
863
+ // still allowed — surfaced as cantTell for the author to decide, not a
864
+ // not-allowed fail.
865
+ const deprecated =
866
+ typeof ariaHelpers.isDeprecatedAttr === 'function' &&
867
+ ariaHelpers.isDeprecatedAttr(name, role);
868
+ if (deprecated) {
869
+ cantTellOccurrences.push(
870
+ helpers.reportOccurrence(el, {
871
+ summary: 'This ARIA attribute is deprecated for this element’s role.',
872
+ hint: 'It is still allowed but discouraged; remove it or use a role that supports it, as a future ARIA version may disallow it.',
873
+ occurrenceOutcome: 'cantTell',
874
+ i18n: {
875
+ summaryKey: 'ariaAllowedAttr_summary_cantTell',
876
+ hintKey: 'ariaAllowedAttr_hint_cantTell',
877
+ params: { attr: name, role }
878
+ },
879
+ data: {
880
+ details: { reasonCode: 'ARIA_ATTR_DEPRECATED', attr: name, role }
881
+ }
882
+ })
883
+ );
884
+ continue;
885
+ }
886
+ failOccurrences.push(
887
+ helpers.reportOccurrence(el, {
888
+ summary: 'This ARIA attribute is not permitted for this element’s role.',
889
+ hint: 'Remove this attribute, or use a role that supports it.',
890
+ occurrenceOutcome: 'fail',
891
+ i18n: {
892
+ summaryKey: 'ariaAllowedAttr_summary_fail',
893
+ hintKey: 'ariaAllowedAttr_hint_fail',
894
+ params: { attr: name, role }
895
+ },
896
+ data: {
897
+ details: { reasonCode: 'ARIA_ATTR_NOT_ALLOWED', attr: name, role }
898
+ }
899
+ })
900
+ );
337
901
  }
338
902
  }
339
903
 
340
904
  if (applicableCount === 0) {
341
905
  return { ruleId: rule.ruleId, outcome: 'notApplicable', severity: 'minor', occurrences: [] };
342
906
  }
343
- if (occurrences.length) {
344
- return {
345
- ruleId: rule.ruleId,
346
- outcome: 'fail',
347
- severity: rule.defaultSeverity || 'moderate',
348
- occurrences
349
- };
350
- }
351
- return { ruleId: rule.ruleId, outcome: 'pass', severity: 'minor', occurrences: [] };
907
+
908
+ const resolved = helpers.resolveTieredOutcome(
909
+ failOccurrences,
910
+ cantTellOccurrences,
911
+ rule.defaultSeverity || 'moderate'
912
+ );
913
+ return { ruleId: rule.ruleId, ...resolved };
352
914
  }
353
915
 
354
916
  module.exports = { id, meta, runInPage };