@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.
- package/CHANGELOG.md +87 -2
- package/README.md +109 -35
- package/bin/surea11y-core.js +20 -0
- package/docs/API_STABILITY.md +26 -0
- package/docs/ARIA_DEPRECATION.md +95 -0
- package/docs/CI_INTEGRATIONS.md +7 -7
- package/docs/ENGINE_OPTIONS.md +1 -1
- package/docs/I18N.md +12 -9
- package/docs/INTEGRATION.md +1 -1
- package/docs/LIMITATIONS.md +1 -1
- package/docs/REPORT.md +1 -1
- package/docs/RULE_CATALOG.md +6 -6
- package/package.json +52 -16
- package/src/baseline.js +0 -0
- package/src/checks/automatic/area-alt-present.js +4 -6
- package/src/checks/automatic/aria-allowed-attr.js +663 -134
- package/src/checks/automatic/aria-allowed-role.js +2 -0
- package/src/checks/automatic/aria-braille-equivalent.js +2 -0
- package/src/checks/automatic/aria-conditional-attr.js +8 -7
- package/src/checks/automatic/aria-deprecated-role.js +107 -38
- package/src/checks/automatic/aria-hidden-body.js +6 -4
- package/src/checks/automatic/aria-hidden-focus.js +12 -13
- package/src/checks/automatic/aria-prohibited-attr.js +98 -105
- package/src/checks/automatic/aria-prohibited-children.js +56 -87
- package/src/checks/automatic/aria-required-attr.js +6 -7
- package/src/checks/automatic/aria-required-children.js +7 -10
- package/src/checks/automatic/aria-required-parent.js +20 -25
- package/src/checks/automatic/aria-role-name-present.js +2 -0
- package/src/checks/automatic/aria-roles-valid.js +33 -6
- package/src/checks/automatic/aria-valid-attr-value.js +18 -15
- package/src/checks/automatic/aria-valid-attr.js +2 -0
- package/src/checks/automatic/autocomplete-valid.js +39 -1
- package/src/checks/automatic/avoid-inline-spacing.js +181 -20
- package/src/checks/automatic/binary-control-name-present.js +13 -3
- package/src/checks/automatic/button-name-present.js +54 -21
- package/src/checks/automatic/canvas-text-alternative-present.js +17 -8
- package/src/checks/automatic/combobox-name-present.js +10 -1
- package/src/checks/automatic/contrast-computable.js +2 -0
- package/src/checks/automatic/contrast-enhanced.js +2 -0
- package/src/checks/automatic/contrast-minimum.js +2 -0
- package/src/checks/automatic/css-orientation-lock.js +21 -27
- package/src/checks/automatic/definition-list-children-valid.js +6 -6
- package/src/checks/automatic/deprecated-elements-not-used.js +4 -2
- package/src/checks/automatic/dialog-name-present.js +18 -11
- package/src/checks/automatic/dlitem-parent-valid.js +2 -0
- package/src/checks/automatic/duplicate-id-aria.js +4 -3
- package/src/checks/automatic/embed-text-alternative-present.js +2 -0
- package/src/checks/automatic/form-control-programmatic-label-present.js +50 -4
- package/src/checks/automatic/form-control-single-label.js +110 -43
- package/src/checks/automatic/html-xml-lang-mismatch.js +2 -0
- package/src/checks/automatic/iframe-focusable-content.js +246 -18
- package/src/checks/automatic/iframe-name-present.js +2 -0
- package/src/checks/automatic/iframe-title-unique.js +3 -1
- package/src/checks/automatic/img-alt-present.js +23 -18
- package/src/checks/automatic/input-image-alt-present.js +101 -52
- package/src/checks/automatic/label-in-name.js +97 -27
- package/src/checks/automatic/language-page-present.js +7 -1
- package/src/checks/automatic/link-in-text-block.js +2 -0
- package/src/checks/automatic/link-name-present.js +52 -17
- package/src/checks/automatic/list-children-valid.js +14 -24
- package/src/checks/automatic/listbox-name-present.js +10 -1
- package/src/checks/automatic/listitem-parent-valid.js +30 -7
- package/src/checks/automatic/menuitem-name-present.js +10 -1
- package/src/checks/automatic/meta-refresh-no-exceptions.js +33 -4
- package/src/checks/automatic/meta-refresh-timing-absent.js +32 -4
- package/src/checks/automatic/meta-viewport-zoom-enabled.js +39 -15
- package/src/checks/automatic/meter-name-present.js +12 -4
- package/src/checks/automatic/nested-interactive-controls-absent.js +177 -25
- package/src/checks/automatic/object-text-alternative-present.js +16 -7
- package/src/checks/automatic/option-name-present.js +10 -1
- package/src/checks/automatic/page-title-present.js +2 -0
- package/src/checks/automatic/progressbar-name-present.js +16 -11
- package/src/checks/automatic/role-img-alt-present.js +4 -4
- package/src/checks/automatic/searchbox-name-present.js +10 -1
- package/src/checks/automatic/server-side-image-map-absent.js +4 -3
- package/src/checks/automatic/slider-name-present.js +13 -2
- package/src/checks/automatic/spinbutton-name-present.js +10 -1
- package/src/checks/automatic/summary-name-present.js +10 -1
- package/src/checks/automatic/svg-image-text-alternative-present.js +2 -0
- package/src/checks/automatic/svg-text-alternative-present.js +17 -5
- package/src/checks/automatic/tab-name-present.js +10 -1
- package/src/checks/automatic/table-headers-attr-valid.js +3 -2
- package/src/checks/automatic/table-th-has-data-cells.js +69 -6
- package/src/checks/automatic/target-size-minimum.js +5 -0
- package/src/checks/automatic/td-has-header.js +24 -1
- package/src/checks/automatic/textbox-name-present.js +10 -1
- package/src/checks/automatic/tooltip-name-present.js +10 -1
- package/src/checks/automatic/treeitem-name-present.js +10 -1
- package/src/checks/automatic/valid-lang.js +18 -3
- package/src/checks/automatic/video-poster-text-alternative-present.js +2 -0
- package/src/checks/manual/accesskeys-manual.js +3 -1
- package/src/checks/manual/area-alt-decorative-manual.js +2 -0
- package/src/checks/manual/area-alt-quality-manual.js +2 -0
- package/src/checks/manual/aria-checked-state-mismatch-manual.js +14 -23
- package/src/checks/manual/aria-text-manual.js +6 -5
- package/src/checks/manual/bypass-blocks-present-manual.js +279 -0
- package/src/checks/manual/canvas-text-alternative-quality-manual.js +2 -0
- package/src/checks/manual/css-hidden-focus.js +184 -9
- package/src/checks/manual/embed-text-alternative-quality-manual.js +18 -13
- package/src/checks/manual/empty-heading-manual.js +17 -17
- package/src/checks/manual/empty-table-header-manual.js +52 -25
- package/src/checks/manual/focus-order-semantics-manual.js +16 -4
- package/src/checks/manual/form-control-programmatic-label-quality-manual.js +2 -0
- package/src/checks/manual/heading-order-manual.js +28 -1
- package/src/checks/manual/identical-links-same-purpose-manual.js +2 -0
- package/src/checks/manual/image-redundant-alt-manual.js +21 -1
- package/src/checks/manual/img-alt-decorative-manual.js +2 -0
- package/src/checks/manual/img-alt-quality-manual.js +2 -0
- package/src/checks/manual/input-image-alt-decorative-manual.js +26 -0
- package/src/checks/manual/input-image-alt-quality-manual.js +2 -0
- package/src/checks/manual/label-title-only-manual.js +29 -22
- package/src/checks/manual/landmark-banner-is-top-level-manual.js +51 -55
- package/src/checks/manual/landmark-contentinfo-is-top-level-manual.js +44 -31
- package/src/checks/manual/landmark-main-is-top-level-manual.js +41 -24
- package/src/checks/manual/landmark-no-duplicate-banner-manual.js +16 -23
- package/src/checks/manual/landmark-no-duplicate-contentinfo-manual.js +14 -21
- package/src/checks/manual/landmark-no-duplicate-main-manual.js +10 -13
- package/src/checks/manual/landmark-one-main-manual.js +12 -23
- package/src/checks/manual/landmark-unique-manual.js +37 -52
- package/src/checks/manual/link-name-quality-manual.js +2 -0
- package/src/checks/manual/media-transcript-present-manual.js +2 -0
- package/src/checks/manual/meta-viewport-large-manual.js +3 -1
- package/src/checks/manual/mouse-only-event-handlers-manual.js +2 -0
- package/src/checks/manual/no-autoplay-audio-manual.js +2 -0
- package/src/checks/manual/object-text-alternative-quality-manual.js +2 -0
- package/src/checks/manual/p-as-heading-manual.js +2 -0
- package/src/checks/manual/page-has-heading-one-manual.js +12 -11
- package/src/checks/manual/page-title-patterns-manual.js +2 -0
- package/src/checks/manual/presentation-role-conflict-manual.js +51 -37
- package/src/checks/manual/region-manual.js +27 -36
- package/src/checks/manual/scope-attr-valid-manual.js +3 -1
- package/src/checks/manual/scrollable-region-focusable-manual.js +2 -0
- package/src/checks/manual/skip-link-manual.js +7 -6
- package/src/checks/manual/svg-text-alternative-quality-manual.js +2 -0
- package/src/checks/manual/tabindex-manual.js +3 -1
- package/src/checks/manual/table-duplicate-name-manual.js +5 -4
- package/src/checks/manual/table-fake-caption-manual.js +24 -3
- package/src/checks/manual/video-caption-manual.js +2 -0
- package/src/checks/manual-review.js +2 -0
- package/src/core.js +11820 -3317
- package/src/index.js +2 -0
- package/src/report.js +51 -9
- package/src/sarif.js +20 -5
- package/surea11y.browser.js +4943 -1388
- package/bin/core.js +0 -473
- package/docs/CLI.md +0 -128
- package/src/catalogs/composites.wcag.js +0 -454
- package/src/checks/automatic/bypass-blocks-present.js +0 -215
- package/src/checks/rules-and-tags.full.csv +0 -19
- package/src/checks/rules-and-tags.full.json +0 -259
- package/src/core/aria-helpers.js +0 -1211
- package/src/core/contrast-helpers.js +0 -1302
- package/src/core/dom-helpers.js +0 -4493
- package/src/core/dom-runner.js +0 -787
- package/src/core/frame-messaging.js +0 -261
- package/src/core/frame-scan.js +0 -190
- package/src/core/rollup-composites.js +0 -127
- package/src/core/rule-meta.js +0 -176
- package/src/coverage/wcag-facets.js +0 -1079
- package/src/coverage/wcag-version-map.js +0 -84
- package/src/i18n/en.js +0 -1228
- package/src/i18n/fr.js +0 -1185
- package/src/policy/contracts.js +0 -18
- package/src/policy/resolvePolicy.js +0 -59
- package/src/policy/schemas/engine-options.schema.json +0 -103
- 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,<area> 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
|