@surea11y/core 1.6.0 → 1.8.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.
- package/CHANGELOG.md +140 -0
- package/README.md +179 -90
- package/docs/ACT_RULE_MAPPING.md +10 -8
- package/docs/API_STABILITY.md +67 -6
- package/docs/BINDING_AUTHORS_GUIDE.md +104 -2
- package/docs/CI_INTEGRATIONS.md +43 -0
- package/docs/DESIGN_CHALLENGES.md +162 -2
- package/docs/EARL.md +100 -0
- package/docs/ENGINE_OPTIONS.md +109 -5
- package/docs/I18N.md +62 -20
- package/docs/INTEGRATION.md +4 -2
- package/docs/JUNIT.md +73 -0
- package/docs/LIMITATIONS.md +4 -1
- package/docs/OUTPUT_SCHEMA.md +62 -11
- package/docs/POLICY.md +1 -1
- package/docs/REPORT.md +7 -2
- package/docs/RULE_AUTHORING.md +83 -17
- package/docs/RULE_CATALOG.md +212 -139
- package/docs/RULE_EXAMPLES.md +2189 -0
- package/docs/RULE_HELPERS.md +390 -0
- package/docs/RULE_TAXONOMY.md +27 -6
- package/docs/SARIF.md +23 -3
- package/docs/WCAG_CONFORMANCE.md +64 -3
- package/package.json +41 -12
- package/profiles/index.js +14 -0
- package/src/checks/automatic/area-alt-present.js +87 -31
- package/src/checks/automatic/aria-allowed-attr.js +6 -0
- package/src/checks/automatic/aria-allowed-role.js +32 -23
- package/src/checks/automatic/aria-braille-equivalent.js +43 -17
- package/src/checks/automatic/aria-conditional-attr.js +17 -10
- package/src/checks/automatic/aria-deprecated-role.js +12 -0
- package/src/checks/automatic/aria-hidden-body.js +1 -1
- package/src/checks/automatic/aria-hidden-focus.js +74 -18
- package/src/checks/automatic/aria-prohibited-attr.js +22 -4
- package/src/checks/automatic/aria-prohibited-children.js +6 -6
- package/src/checks/automatic/aria-required-attr.js +88 -12
- package/src/checks/automatic/aria-required-children.js +33 -16
- package/src/checks/automatic/aria-required-parent.js +32 -6
- package/src/checks/automatic/aria-role-name-present.js +20 -3
- package/src/checks/automatic/aria-roles-valid.js +52 -21
- package/src/checks/automatic/aria-valid-attr-value.js +89 -24
- package/src/checks/automatic/aria-valid-attr.js +14 -9
- package/src/checks/automatic/autocomplete-valid.js +152 -26
- package/src/checks/automatic/avoid-inline-spacing.js +207 -15
- package/src/checks/automatic/button-name-present.js +2 -1
- package/src/checks/automatic/canvas-text-alternative-present.js +105 -14
- package/src/checks/automatic/combobox-name-present.js +34 -51
- package/src/checks/automatic/contrast-computable.js +45 -4
- package/src/checks/automatic/contrast-enhanced.js +16 -4
- package/src/checks/automatic/contrast-minimum.js +57 -11
- package/src/checks/automatic/css-orientation-lock.js +171 -12
- package/src/checks/automatic/definition-list-children-valid.js +67 -23
- package/src/checks/automatic/deprecated-elements-not-used.js +43 -38
- package/src/checks/automatic/dialog-name-present.js +28 -9
- package/src/checks/automatic/duplicate-id-aria.js +5 -0
- package/src/checks/automatic/duplicate-id.js +19 -10
- package/src/checks/automatic/form-control-single-label.js +9 -0
- package/src/checks/automatic/identical-iframes-same-purpose.js +229 -0
- package/src/checks/automatic/iframe-focusable-content.js +12 -4
- package/src/checks/automatic/iframe-title-unique.js +36 -81
- package/src/checks/automatic/input-image-alt-present.js +32 -20
- package/src/checks/automatic/label-in-name.js +78 -69
- package/src/checks/automatic/language-page-present.js +12 -6
- package/src/checks/automatic/link-in-text-block.js +512 -44
- package/src/checks/automatic/link-name-present.js +13 -5
- package/src/checks/automatic/list-children-valid.js +18 -1
- package/src/checks/automatic/listbox-name-present.js +19 -49
- package/src/checks/automatic/listitem-parent-valid.js +4 -3
- package/src/checks/automatic/meta-refresh-no-exceptions.js +3 -3
- package/src/checks/automatic/page-title-present.js +16 -4
- package/src/checks/automatic/progressbar-name-present.js +11 -1
- package/src/checks/automatic/{role-img-alt-present.js → role-img-text-alternative-present.js} +9 -5
- package/src/checks/automatic/searchbox-name-present.js +32 -49
- package/src/checks/automatic/server-side-image-map-absent.js +48 -28
- package/src/checks/automatic/slider-name-present.js +38 -52
- package/src/checks/automatic/spinbutton-name-present.js +32 -49
- package/src/checks/automatic/target-size-minimum.js +84 -16
- package/src/checks/automatic/td-has-header.js +60 -23
- package/src/checks/automatic/text-spacing-content-loss.js +548 -0
- package/src/checks/automatic/textbox-name-present.js +32 -49
- package/src/checks/automatic/valid-lang.js +15 -10
- package/src/checks/manual/area-alt-quality-manual.js +113 -31
- package/src/checks/manual/canvas-text-alternative-quality-manual.js +0 -2
- package/src/checks/manual/css-focus-indicator-suppressed-manual.js +23 -10
- package/src/checks/manual/css-hidden-focus.js +215 -7
- package/src/checks/manual/form-control-label-quality-manual.js +243 -29
- package/src/checks/manual/heading-order-manual.js +9 -1
- package/src/checks/manual/heading-quality-manual.js +143 -9
- package/src/checks/manual/img-alt-decorative-manual.js +6 -3
- package/src/checks/manual/input-image-alt-quality-manual.js +81 -13
- package/src/checks/manual/landmark-complementary-is-top-level-manual.js +231 -0
- package/src/checks/manual/link-name-quality-manual.js +130 -4
- package/src/checks/manual/media-transcript-present-manual.js +65 -8
- package/src/checks/manual/mouse-only-event-handlers-manual.js +66 -5
- package/src/checks/manual/no-autoplay-audio-manual.js +93 -6
- package/src/checks/manual/p-as-heading-manual.js +89 -44
- package/src/checks/manual/page-title-patterns-manual.js +77 -8
- package/src/checks/manual/password-paste-enabled-manual.js +255 -0
- package/src/checks/manual/skip-link-manual.js +42 -14
- package/src/checks/manual/table-fake-caption-manual.js +32 -1
- package/src/checks/manual/video-caption-manual.js +47 -24
- package/src/checks/manual-review.js +0 -4
- package/src/core.js +18061 -46194
- package/src/coverage/en301549-map.js +187 -0
- package/src/coverage/standards.js +279 -0
- package/src/coverage/wcag-facets.js +1119 -0
- package/src/coverage/wcag-version-map.js +101 -0
- package/src/earl.js +144 -0
- package/src/en301549.js +33 -0
- package/src/junit.js +321 -0
- package/src/profile-kit.js +163 -0
- package/src/report.js +343 -74
- package/src/sarif.js +56 -5
- package/src/wcag.js +105 -0
- package/surea11y.browser.js +11 -41039
- package/surea11y.i18n.de.js +2 -21
- package/surea11y.i18n.es.js +2 -21
- package/surea11y.i18n.fr.js +2 -21
- package/surea11y.i18n.ja.js +3 -0
- package/src/checks/manual/area-alt-decorative-manual.js +0 -255
package/docs/RULE_CATALOG.md
CHANGED
|
@@ -2,29 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
Generated from the compiled engine's own catalog (`getChecksCatalog()`/`getRulesCatalog()`) and each rule's source header. Run `node scripts/generate-rule-catalog.js` after `npm run build` to regenerate this file whenever rules change. Do not hand-edit.
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**133 rules total: 80 automatic (WCAG-normative, can return `fail`), 53 manual (advisory/judgment-required, capped at `cantTell`). 107 carry at least one formal WCAG Success Criterion mapping.**
|
|
6
6
|
|
|
7
7
|
The tables below are an index; [rule reference](#rule-reference) carries each rule's description, what it applies to and what it expects.
|
|
8
8
|
|
|
9
9
|
See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`severity` mean on a scan result, and [`WCAG_CONFORMANCE.md`](./WCAG_CONFORMANCE.md) for how these roll up to an SC-level conformance claim. For WCAG-facet-level coverage-gap tracking (which parts of an SC are and aren't automatable yet), see `coverage/coverage-report.md` instead: that one is organized by facet, this one by rule.
|
|
10
10
|
|
|
11
|
-
## Automatic rules (
|
|
11
|
+
## Automatic rules (80), can return `fail`
|
|
12
12
|
|
|
13
13
|
| Rule ID | Title | WCAG SC | Level | Confidence | Default severity |
|
|
14
14
|
|---|---|---|---|---|---|
|
|
15
|
-
| [`area-alt-present`](#area-alt-present) | <area> must have an
|
|
15
|
+
| [`area-alt-present`](#area-alt-present) | <area> must have an accessible name | 1.1.1 | A | high | serious |
|
|
16
16
|
| [`aria-allowed-attr`](#aria-allowed-attr) | aria-* attributes must be permitted for the element’s role | 4.1.2 | A | medium | moderate |
|
|
17
|
-
| [`aria-allowed-role`](#aria-allowed-role) | Explicit role must be permitted for its host element |
|
|
18
|
-
| [`aria-braille-equivalent`](#aria-braille-equivalent) | aria-braillelabel/aria-brailleroledescription must have a non-braille equivalent | 4.1.2 | A | high |
|
|
19
|
-
| [`aria-conditional-attr`](#aria-conditional-attr) | aria-errormessage requires aria-invalid to be set to a non-false value | 4.1.2 | A | high |
|
|
17
|
+
| [`aria-allowed-role`](#aria-allowed-role) | Explicit role must be permitted for its host element | — | — | high | moderate |
|
|
18
|
+
| [`aria-braille-equivalent`](#aria-braille-equivalent) | aria-braillelabel/aria-brailleroledescription must have a non-braille equivalent | 4.1.2 | A | high | moderate |
|
|
19
|
+
| [`aria-conditional-attr`](#aria-conditional-attr) | aria-errormessage requires aria-invalid to be set to a non-false value | 4.1.2 | A | high | moderate |
|
|
20
20
|
| [`aria-deprecated-role`](#aria-deprecated-role) | role attribute should not use a deprecated or author-discouraged ARIA role | 4.1.2 | A | high | moderate |
|
|
21
21
|
| [`aria-hidden-body`](#aria-hidden-body) | The document <body> must not be aria-hidden | 1.3.1, 4.1.2 | A | high | critical |
|
|
22
22
|
| [`aria-hidden-focus`](#aria-hidden-focus) | ARIA hidden elements must not be focusable | 2.4.7, 4.1.2 | AA | high | serious |
|
|
23
23
|
| [`aria-prohibited-attr`](#aria-prohibited-attr) | ARIA naming attributes must not be used on roles that prohibit them | 4.1.2 | A | high | moderate |
|
|
24
|
-
| [`aria-prohibited-children`](#aria-prohibited-children) | Container roles must not own a child with a disallowed role |
|
|
24
|
+
| [`aria-prohibited-children`](#aria-prohibited-children) | Container roles must not own a child with a disallowed role | 1.3.1 | A | medium | moderate |
|
|
25
25
|
| [`aria-required-attr`](#aria-required-attr) | Roles with a required ARIA state/property must carry it | 4.1.2 | A | high | serious |
|
|
26
|
-
| [`aria-required-children`](#aria-required-children) | Container roles must own at least one required child role |
|
|
27
|
-
| [`aria-required-parent`](#aria-required-parent) | Roles requiring a specific context role must be in that context |
|
|
26
|
+
| [`aria-required-children`](#aria-required-children) | Container roles must own at least one required child role | 1.3.1 | A | medium | moderate |
|
|
27
|
+
| [`aria-required-parent`](#aria-required-parent) | Roles requiring a specific context role must be in that context | 1.3.1 | A | medium | moderate |
|
|
28
28
|
| [`aria-role-name-present`](#aria-role-name-present) | ARIA roles that require an accessible name have one | 4.1.2 | A | high | serious |
|
|
29
29
|
| [`aria-roles-valid`](#aria-roles-valid) | role attribute must be a valid, non-abstract ARIA role | 4.1.2 | A | high | serious |
|
|
30
30
|
| [`aria-valid-attr`](#aria-valid-attr) | aria-* attributes must be real, defined ARIA attributes | 4.1.2 | A | high | serious |
|
|
@@ -40,7 +40,7 @@ See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`sever
|
|
|
40
40
|
| [`contrast-minimum`](#contrast-minimum) | Text meets minimum color contrast (AA) | 1.4.3 | AA | high | serious |
|
|
41
41
|
| [`css-orientation-lock`](#css-orientation-lock) | CSS must not lock the page to a single orientation | 1.3.4 | AA | high | serious |
|
|
42
42
|
| [`definition-list-children-valid`](#definition-list-children-valid) | Description lists must be structured correctly | 1.3.1 | A | high | serious |
|
|
43
|
-
| [`deprecated-elements-not-used`](#deprecated-elements-not-used) |
|
|
43
|
+
| [`deprecated-elements-not-used`](#deprecated-elements-not-used) | Scrolling <marquee> content must be possible to pause, stop, or hide | 2.2.2 | A | high | serious |
|
|
44
44
|
| [`dialog-name-present`](#dialog-name-present) | Dialogs have an accessible name | 4.1.2 | A | high | serious |
|
|
45
45
|
| [`dlitem-parent-valid`](#dlitem-parent-valid) | Description-list items must be inside a description list | 1.3.1 | A | high | serious |
|
|
46
46
|
| [`duplicate-id`](#duplicate-id) | IDs must be unique | 4.1.1 | A | high | moderate |
|
|
@@ -50,19 +50,20 @@ See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`sever
|
|
|
50
50
|
| [`form-control-single-label`](#form-control-single-label) | Form controls must not have multiple labels | 3.3.2 | A | high | moderate |
|
|
51
51
|
| [`html-lang-attr-present`](#html-lang-attr-present) | Page language is declared | 3.1.1 | A | high | serious |
|
|
52
52
|
| [`html-xml-lang-mismatch`](#html-xml-lang-mismatch) | lang and xml:lang must not disagree | 3.1.1 | A | high | serious |
|
|
53
|
+
| [`identical-iframes-same-purpose`](#identical-iframes-same-purpose) | Frames with the same name embed the same resource | 4.1.2 | A | medium | moderate |
|
|
53
54
|
| [`iframe-focusable-content`](#iframe-focusable-content) | Frames with tabindex="-1" must not contain focusable content | 2.1.1 | A | high | moderate |
|
|
54
55
|
| [`iframe-name-present`](#iframe-name-present) | Frames have an accessible name | 4.1.2 | A | high | serious |
|
|
55
|
-
| [`iframe-title-unique`](#iframe-title-unique) | Frame
|
|
56
|
+
| [`iframe-title-unique`](#iframe-title-unique) | Frame title uniqueness (deprecated) | 4.1.2 | A | high | moderate |
|
|
56
57
|
| [`img-alt-present`](#img-alt-present) | <img> must have an alt attribute | 1.1.1 | A | high | serious |
|
|
57
58
|
| [`input-image-alt-present`](#input-image-alt-present) | <input type="image"> must have an alt attribute | 1.1.1 | A | high | serious |
|
|
58
59
|
| [`label-in-name`](#label-in-name) | Label in Name: accessible name contains visible text | 2.5.3 | A | high | serious |
|
|
59
60
|
| [`link-in-text-block`](#link-in-text-block) | Links in text blocks must be distinguishable from surrounding text without relying on color alone | 1.4.1 | A | high | serious |
|
|
60
|
-
| [`link-name-present`](#link-name-present) | Links have an accessible name | 4.1.2 | A | high | serious |
|
|
61
|
+
| [`link-name-present`](#link-name-present) | Links have an accessible name | 2.4.4, 4.1.2 | A | high | serious |
|
|
61
62
|
| [`list-children-valid`](#list-children-valid) | Lists must only directly contain list items | 1.3.1 | A | high | serious |
|
|
62
63
|
| [`listbox-name-present`](#listbox-name-present) | Listboxes have an accessible name | 4.1.2 | A | high | serious |
|
|
63
64
|
| [`listitem-parent-valid`](#listitem-parent-valid) | List items must be inside a list container | 1.3.1 | A | high | serious |
|
|
64
65
|
| [`menuitem-name-present`](#menuitem-name-present) | Menu items have an accessible name | 4.1.2 | A | high | serious |
|
|
65
|
-
| [`meta-refresh-no-exceptions`](#meta-refresh-no-exceptions) | Page must not use a meta refresh
|
|
66
|
+
| [`meta-refresh-no-exceptions`](#meta-refresh-no-exceptions) | Page must not use a timed meta refresh (AAA) | 2.2.4, 3.2.5 | AAA | high | moderate |
|
|
66
67
|
| [`meta-refresh-timing-absent`](#meta-refresh-timing-absent) | Page must not use a timed meta refresh | 2.2.1 | A | high | serious |
|
|
67
68
|
| [`meta-viewport-zoom-enabled`](#meta-viewport-zoom-enabled) | Viewport meta tag must not disable zoom | 1.4.4 | AA | high | serious |
|
|
68
69
|
| [`meter-name-present`](#meter-name-present) | Meters have an accessible name | 1.1.1 | A | high | serious |
|
|
@@ -74,7 +75,7 @@ See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`sever
|
|
|
74
75
|
| [`progressbar-name-present`](#progressbar-name-present) | Progress bars have an accessible name | 1.1.1 | A | high | serious |
|
|
75
76
|
| [`role-img-text-alternative-present`](#role-img-text-alternative-present) | [role="img"/"graphics-symbol"/"graphics-document"] must have an accessible text alternative | 1.1.1 | A | high | serious |
|
|
76
77
|
| [`searchbox-name-present`](#searchbox-name-present) | Searchboxes have an accessible name | 4.1.2 | A | high | serious |
|
|
77
|
-
| [`server-side-image-map-absent`](#server-side-image-map-absent) |
|
|
78
|
+
| [`server-side-image-map-absent`](#server-side-image-map-absent) | Server-side image maps must have a keyboard-operable alternative | 2.1.1 | A | high | serious |
|
|
78
79
|
| [`slider-name-present`](#slider-name-present) | Sliders have an accessible name | 4.1.2 | A | high | serious |
|
|
79
80
|
| [`spinbutton-name-present`](#spinbutton-name-present) | Spinbuttons have an accessible name | 4.1.2 | A | high | serious |
|
|
80
81
|
| [`summary-name-present`](#summary-name-present) | Summary elements have an accessible name | 4.1.2 | A | high | serious |
|
|
@@ -85,19 +86,19 @@ See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`sever
|
|
|
85
86
|
| [`table-th-has-data-cells`](#table-th-has-data-cells) | <th> elements must describe at least one data cell | 1.3.1 | A | high | moderate |
|
|
86
87
|
| [`target-size-minimum`](#target-size-minimum) | Pointer targets must be at least 24x24px large, or leave sufficient distance to other targets | 2.5.8 | AA | medium | serious |
|
|
87
88
|
| [`td-has-header`](#td-has-header) | Data cells in large tables must have an associated header | 1.3.1 | A | high | serious |
|
|
89
|
+
| [`text-spacing-content-loss`](#text-spacing-content-loss) | Text stays readable when the user increases text spacing | 1.4.12 | AA | medium | serious |
|
|
88
90
|
| [`textbox-name-present`](#textbox-name-present) | Textboxes have an accessible name | 4.1.2 | A | high | serious |
|
|
89
91
|
| [`tooltip-name-present`](#tooltip-name-present) | Tooltips have an accessible name | 4.1.2 | A | high | serious |
|
|
90
92
|
| [`treeitem-name-present`](#treeitem-name-present) | Tree items have an accessible name | 4.1.2 | A | high | serious |
|
|
91
93
|
| [`valid-lang`](#valid-lang) | Element lang attribute must be syntactically valid | 3.1.2 | AA | high | moderate |
|
|
92
94
|
| [`video-poster-text-alternative-present`](#video-poster-text-alternative-present) | <video> poster must have a text alternative | 1.1.1 | A | medium | serious |
|
|
93
95
|
|
|
94
|
-
## Manual rules (
|
|
96
|
+
## Manual rules (53), advisory, capped at `cantTell`
|
|
95
97
|
|
|
96
98
|
| Rule ID | Title | WCAG SC | Level | Confidence | Default severity |
|
|
97
99
|
|---|---|---|---|---|---|
|
|
98
100
|
| [`accesskeys`](#accesskeys) | accesskey values must be unique | — | — | medium | minor |
|
|
99
|
-
| [`area-alt-
|
|
100
|
-
| [`area-alt-quality`](#area-alt-quality) | <area> alt text must be appropriate (manual review) | 1.1.1 | A | medium | minor |
|
|
101
|
+
| [`area-alt-quality`](#area-alt-quality) | <area> text alternative must be appropriate (manual review) | 1.1.1 | A | medium | minor |
|
|
101
102
|
| [`aria-checked-state-mismatch`](#aria-checked-state-mismatch) | Native checkbox/radio aria-checked should match its actual state | 4.1.2 | A | medium | moderate |
|
|
102
103
|
| [`aria-text`](#aria-text) | role="text" elements should have no focusable descendants | — | — | medium | minor |
|
|
103
104
|
| [`bypass-blocks-present`](#bypass-blocks-present) | Page must provide a way to bypass repeated blocks | 2.4.1 | A | medium | moderate |
|
|
@@ -117,9 +118,10 @@ See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`sever
|
|
|
117
118
|
| [`img-alt-decorative`](#img-alt-decorative) | Excluded <img>/<canvas>/<svg> must be decorative (manual review) | 1.1.1 | A | medium | minor |
|
|
118
119
|
| [`img-alt-quality`](#img-alt-quality) | <img> alt text must be appropriate (manual review) | 1.1.1 | A | medium | minor |
|
|
119
120
|
| [`input-image-alt-decorative`](#input-image-alt-decorative) | <input type="image"> with alt="" must be appropriate (manual review) | 1.1.1 | A | medium | minor |
|
|
120
|
-
| [`input-image-alt-quality`](#input-image-alt-quality) | <input type="image">
|
|
121
|
+
| [`input-image-alt-quality`](#input-image-alt-quality) | <input type="image"> text alternative must be appropriate (manual review) | 1.1.1 | A | medium | minor |
|
|
121
122
|
| [`label-title-only`](#label-title-only) | Form controls should not use title as their only label | — | — | medium | minor |
|
|
122
123
|
| [`landmark-banner-is-top-level`](#landmark-banner-is-top-level) | Banner landmark must be top-level | — | — | medium | minor |
|
|
124
|
+
| [`landmark-complementary-is-top-level`](#landmark-complementary-is-top-level) | Complementary landmark must be top-level | — | — | medium | minor |
|
|
123
125
|
| [`landmark-contentinfo-is-top-level`](#landmark-contentinfo-is-top-level) | Contentinfo landmark must be top-level | — | — | medium | minor |
|
|
124
126
|
| [`landmark-main-is-top-level`](#landmark-main-is-top-level) | Main landmark must be top-level | — | — | medium | minor |
|
|
125
127
|
| [`landmark-no-duplicate-banner`](#landmark-no-duplicate-banner) | Page must not have more than one banner landmark | — | — | medium | minor |
|
|
@@ -134,9 +136,10 @@ See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`sever
|
|
|
134
136
|
| [`mouse-only-event-handlers`](#mouse-only-event-handlers) | Pointer-only inline event handlers should have a keyboard-reachable equivalent | 2.1.1 | A | low | moderate |
|
|
135
137
|
| [`no-autoplay-audio`](#no-autoplay-audio) | Autoplaying audio should provide a pause/stop or volume-control mechanism | 1.4.2 | A | low | moderate |
|
|
136
138
|
| [`object-text-alternative-quality`](#object-text-alternative-quality) | <object> text alternative must be appropriate (manual review) | 1.1.1 | A | medium | minor |
|
|
137
|
-
| [`p-as-heading`](#p-as-heading) |
|
|
139
|
+
| [`p-as-heading`](#p-as-heading) | Text styled to look like a heading should probably be a real heading | 1.3.1 | A | low | minor |
|
|
138
140
|
| [`page-has-heading-one`](#page-has-heading-one) | Page should have a level-one heading | — | — | medium | minor |
|
|
139
141
|
| [`page-title-patterns`](#page-title-patterns) | Page title patterns that may be insufficiently descriptive | 2.4.2 | A | medium | minor |
|
|
142
|
+
| [`password-paste-enabled`](#password-paste-enabled) | Authentication fields must not block pasting | 3.3.8 | AA | medium | serious |
|
|
140
143
|
| [`presentation-role-conflict`](#presentation-role-conflict) | Presentational role must not conflict with a global ARIA attribute or focusability | — | — | medium | minor |
|
|
141
144
|
| [`region`](#region) | Page content should be inside a landmark region | — | — | medium | minor |
|
|
142
145
|
| [`scope-attr-valid`](#scope-attr-valid) | scope attribute must have a valid value | — | — | medium | minor |
|
|
@@ -148,20 +151,20 @@ See [`OUTPUT_SCHEMA.md`](./OUTPUT_SCHEMA.md) for what `type`/`confidence`/`sever
|
|
|
148
151
|
| [`table-fake-caption`](#table-fake-caption) | A table's first row should not stand in for a real <caption> | 1.3.1 | A | low | minor |
|
|
149
152
|
| [`video-caption`](#video-caption) | Prerecorded video should provide a captions track | 1.2.2 | A | low | moderate |
|
|
150
153
|
|
|
151
|
-
## Composite (WCAG-SC rollup) rules (
|
|
154
|
+
## Composite (WCAG-SC rollup) rules (34)
|
|
152
155
|
|
|
153
156
|
Composite rules aren't individually authored. They're generated rollups over the atomic rules above, one per WCAG Success Criterion with automatable coverage. See [`WCAG_CONFORMANCE.md`](./WCAG_CONFORMANCE.md) for rollup semantics.
|
|
154
157
|
|
|
155
158
|
| Composite ID | Title | Description | WCAG SC | Level | # atomic rules rolled up |
|
|
156
159
|
|---|---|---|---|---|---|
|
|
157
|
-
| `wcag-1.1.1-non-text-content` | Non-text content: text alternatives | Rollup of checks ensuring non-text content has an appropriate text alternative. | 1.1.1 | A |
|
|
160
|
+
| `wcag-1.1.1-non-text-content` | Non-text content: text alternatives | Rollup of checks ensuring non-text content has an appropriate text alternative. | 1.1.1 | A | 21 |
|
|
158
161
|
| `wcag-1.2.1-audio-only-video-only-prerecorded` | Audio-only and video-only (prerecorded): transcript | Rollup of checks for transcript availability for prerecorded audio-only/video-only media. | 1.2.1 | A | 1 |
|
|
159
162
|
| `wcag-1.2.2-captions-prerecorded` | Captions (Prerecorded) | Rollup of checks for captions-track evidence on prerecorded video. | 1.2.2 | A | 1 |
|
|
160
|
-
| `wcag-1.3.1-info-and-relationships` | Info and Relationships | Rollup of checks ensuring information, structure, and relationships conveyed through presentation are programmatically determinable. | 1.3.1 | A |
|
|
163
|
+
| `wcag-1.3.1-info-and-relationships` | Info and Relationships | Rollup of checks ensuring information, structure, and relationships conveyed through presentation are programmatically determinable. | 1.3.1 | A | 14 |
|
|
161
164
|
| `wcag-1.3.4-orientation` | Orientation | Rollup of checks ensuring content does not restrict its view to a single display orientation. | 1.3.4 | AA | 1 |
|
|
162
165
|
| `wcag-1.3.5-identify-input-purpose` | Identify Input Purpose | Rollup of checks ensuring the autocomplete attribute correctly identifies input purpose. | 1.3.5 | AA | 1 |
|
|
163
166
|
| `wcag-1.4.1-use-of-color` | Use of Color | Rollup of checks ensuring color is not used as the only visual means of conveying information. | 1.4.1 | A | 1 |
|
|
164
|
-
| `wcag-1.4.12-text-spacing` | Text Spacing | Rollup of checks ensuring
|
|
167
|
+
| `wcag-1.4.12-text-spacing` | Text Spacing | Rollup of checks ensuring text spacing can be increased without losing content. | 1.4.12 | AA | 2 |
|
|
165
168
|
| `wcag-1.4.2-audio-control` | Audio Control | Rollup of checks for a pause/stop or volume-control mechanism on autoplaying audio. | 1.4.2 | A | 1 |
|
|
166
169
|
| `wcag-1.4.3-contrast-minimum` | Contrast: minimum | Rollup of checks for minimum text contrast. | 1.4.3 | AA | 2 |
|
|
167
170
|
| `wcag-1.4.4-resize-text` | Resize Text | Rollup of checks ensuring the viewport meta tag does not prevent users from zooming text up to 200%. | 1.4.4 | AA | 1 |
|
|
@@ -174,7 +177,7 @@ Composite rules aren't individually authored. They're generated rollups over the
|
|
|
174
177
|
| `wcag-2.4.1-bypass-blocks` | Bypass Blocks | Rollup of checks ensuring the page provides a way to bypass repeated blocks of content. | 2.4.1 | A | 1 |
|
|
175
178
|
| `wcag-2.4.2-page-titled` | Page titled | Rollup of checks ensuring documents have a meaningful page title. | 2.4.2 | A | 2 |
|
|
176
179
|
| `wcag-2.4.3-focus-order` | Focus order | Rollup of checks ensuring focus moves through content in a meaningful order. | 2.4.3 | A | 1 |
|
|
177
|
-
| `wcag-2.4.4-link-purpose-in-context` | Link Purpose (In Context) | Rollup of checks flagging links whose text alone is a known non-descriptive/generic phrase. | 2.4.4 | A |
|
|
180
|
+
| `wcag-2.4.4-link-purpose-in-context` | Link Purpose (In Context) | Rollup of checks flagging links with no accessible name, or whose text alone is a known non-descriptive/generic phrase. | 2.4.4 | A | 2 |
|
|
178
181
|
| `wcag-2.4.6-headings-and-labels` | Headings and Labels | Rollup of checks flagging headings whose text is a placeholder rather than a description of the content that follows. | 2.4.6 | AA | 2 |
|
|
179
182
|
| `wcag-2.4.7-focus-visible` | Focus visible | Rollup of checks ensuring keyboard focus is not hidden and remains perceivable. | 2.4.7 | AA | 4 |
|
|
180
183
|
| `wcag-2.4.9-link-purpose-link-only` | Link Purpose (Link Only) | Rollup of checks ensuring links with the same accessible name serve the same purpose (AAA). | 2.4.9 | AAA | 1 |
|
|
@@ -184,9 +187,10 @@ Composite rules aren't individually authored. They're generated rollups over the
|
|
|
184
187
|
| `wcag-3.1.2-language-of-parts` | Language of Parts | Rollup of checks ensuring elements whose language differs from the page default declare it correctly. | 3.1.2 | AA | 1 |
|
|
185
188
|
| `wcag-3.2.5-change-on-request` | Change on Request | Rollup of checks ensuring context changes only happen at the user's request (AAA). | 3.2.5 | AAA | 1 |
|
|
186
189
|
| `wcag-3.3.2-labels-or-instructions` | Labels or Instructions | Rollup of checks ensuring form controls have unambiguous labeling. | 3.3.2 | A | 2 |
|
|
190
|
+
| `wcag-3.3.8-accessible-authentication-minimum` | Accessible Authentication (Minimum) | Rollup of checks ensuring an authentication step leaves the mechanisms that help a user through it in place. | 3.3.8 | AA | 1 |
|
|
187
191
|
| `wcag-4.1.1-parsing` | Parsing | Rollup of checks ensuring id values are unique. WCAG 2.0/2.1 only: SC 4.1.1 was removed in WCAG 2.2, so this composite carries the wcag22-removed tag. | 4.1.1 | A | 1 |
|
|
188
|
-
| `wcag-4.1.2-aria-validity` | Name, role, value: ARIA validity | Rollup of checks that ARIA role and attribute usage conforms to the WAI-ARIA specification (valid roles, valid attributes, valid values, required attributes
|
|
189
|
-
| `wcag-4.1.2-name` | Name, role, value: accessible name | Rollup of checks that common interactive elements expose a non-empty accessible name. | 4.1.2 | A |
|
|
192
|
+
| `wcag-4.1.2-aria-validity` | Name, role, value: ARIA validity | Rollup of checks that ARIA role and attribute usage conforms to the WAI-ARIA specification (valid roles, valid attributes, valid values, required attributes, unique ARIA-referenced ids). | 4.1.2 | A | 13 |
|
|
193
|
+
| `wcag-4.1.2-name` | Name, role, value: accessible name | Rollup of checks that common interactive elements expose a non-empty accessible name. | 4.1.2 | A | 24 |
|
|
190
194
|
|
|
191
195
|
## Rule reference
|
|
192
196
|
|
|
@@ -204,41 +208,29 @@ Checks that no two elements on the page share the same accesskey attribute value
|
|
|
204
208
|
|
|
205
209
|
**Expectation.** Every accesskey value on the page is unique. Duplicate accesskeys make keyboard-shortcut activation ambiguous: only one of the elements sharing the key can actually be reached by it, and which one is browser/platform-dependent.
|
|
206
210
|
|
|
207
|
-
### `area-alt-decorative`
|
|
208
|
-
|
|
209
|
-
**<area> with alt="" must be decorative (manual review)**
|
|
210
|
-
|
|
211
|
-
manual · WCAG 1.1.1 (A) · confidence medium · default severity minor
|
|
212
|
-
|
|
213
|
-
Flags <area> elements with empty alt for human review that they are decorative/non-informative.
|
|
214
|
-
|
|
215
|
-
**Applies to.** Applies to <area> elements whose alt attribute is present but empty once trimmed, the markup that declares a hotspot decorative. The <area> must belong to a <map> that an <img usemap> actually references, and both that <img> and the <area> itself must be included in the accessibility tree; an <area> in an unused map is out of scope. role="presentation"/"none" takes an element out unless it is focusable.
|
|
216
|
-
|
|
217
|
-
**Expectation.** Human review is required to confirm that the provided text alternative is accurate and appropriate.
|
|
218
|
-
|
|
219
211
|
### `area-alt-present`
|
|
220
212
|
|
|
221
|
-
**<area> must have an
|
|
213
|
+
**<area> must have an accessible name**
|
|
222
214
|
|
|
223
215
|
automatic · WCAG 1.1.1 (A) · confidence high · default severity serious
|
|
224
216
|
|
|
225
|
-
Checks that <area> elements
|
|
217
|
+
Checks that <area> elements have a non-empty accessible name via alt, aria-label/aria-labelledby, or title.
|
|
226
218
|
|
|
227
|
-
**Applies to.** Applies to <area> elements that: 1) are in a <map> that is referenced by an <img usemap>, AND 2) the referencing <img> is
|
|
219
|
+
**Applies to.** Applies to <area> elements that: 1) are in a <map> that is referenced by an <img usemap>, AND 2) carry a non-empty href (an <area> with no href is not a hyperlink at all per the HTML spec, and has nothing for this rule to name), AND 3) the referencing <img> is actually rendered (hidden/display:none/ visibility exclude it; aria-hidden does not, since <area> is not a DOM descendant of <img>), AND 4) the <area> itself is eligible in the accessibility tree. hidden, display:none and inert on the <area> or its <map> do not exclude it: neither element generates a box, so a real browser's image-map hit-testing ignores all three there (verified against Chromium and Firefox); only those mechanisms on a genuine ancestor of the whole <img>+<map> pairing do.
|
|
228
220
|
|
|
229
|
-
**Expectation.** Each applicable <area> element has
|
|
221
|
+
**Expectation.** Each applicable <area> element has a non-empty accessible name, from alt, aria-label/aria-labelledby, or title. An <area> in a used map is always a link, so alt="" is not decorative here as it is on <img>: an empty alt fails the same as a missing one unless another mechanism names it.
|
|
230
222
|
|
|
231
223
|
### `area-alt-quality`
|
|
232
224
|
|
|
233
|
-
**<area>
|
|
225
|
+
**<area> text alternative must be appropriate (manual review)**
|
|
234
226
|
|
|
235
227
|
manual · WCAG 1.1.1 (A) · confidence medium · default severity minor
|
|
236
228
|
|
|
237
|
-
Flags <area> elements with non-empty alt
|
|
229
|
+
Flags <area> elements with a non-empty text alternative (alt, aria-label, aria-labelledby or title) for human review of appropriateness.
|
|
238
230
|
|
|
239
|
-
**Applies to.** Applies to <area> elements
|
|
231
|
+
**Applies to.** Applies to <area> elements that get a non-empty text alternative from any source: aria-labelledby (resolving to text), aria-label, alt or title. The <area> must carry a non-empty href (otherwise it is not a hyperlink at all per the HTML spec) and belong to a <map> that an <img usemap> actually references; an <area> in an unused map is out of scope. The referencing <img> must actually be rendered (hidden/display:none/ visibility exclude it; aria-hidden does not, since <area> is not a DOM descendant of <img>), and the <area> itself must be eligible: hidden, display:none and inert on the <area> or its <map> do not exclude it, since neither generates a box and a real browser's image-map hit-testing ignores all three there (verified against Chromium and Firefox); only those mechanisms on a genuine ancestor of the whole <img>+<map> pairing do. role="presentation"/"none" takes an element out unless it is focusable.
|
|
240
232
|
|
|
241
|
-
**Expectation.** Human review is required to confirm that the provided text alternative is accurate and appropriate.
|
|
233
|
+
**Expectation.** Human review is required to confirm that the provided text alternative is accurate and appropriate. Each occurrence lists every source present (data.details.sources), so the reviewer checks each one: a title or aria-label that is not the name still reaches some users.
|
|
242
234
|
|
|
243
235
|
### `aria-allowed-attr`
|
|
244
236
|
|
|
@@ -256,19 +248,19 @@ Checks that every recognized aria-* attribute present on an element with an expl
|
|
|
256
248
|
|
|
257
249
|
**Explicit role must be permitted for its host element**
|
|
258
250
|
|
|
259
|
-
automatic · WCAG
|
|
251
|
+
automatic · no formal WCAG SC mapping · confidence high · default severity moderate
|
|
260
252
|
|
|
261
253
|
Checks that an explicit role="" attribute is one of the roles the ARIA-in-HTML specification permits for the host element (e.g. role="tab" is not permitted on <nav>).
|
|
262
254
|
|
|
263
255
|
**Applies to.** Applies to elements with an explicit, valid, non-abstract role, where the host element/attribute combination has an asserted permitted-roles constraint in the ARIA-in-HTML table (src/core/aria-helpers.js ALLOWED_ROLES_BY_ELEMENT).
|
|
264
256
|
|
|
265
|
-
**Expectation.** The explicit role is one of the roles the ARIA-in-HTML specification permits for that host element.
|
|
257
|
+
**Expectation.** The explicit role is one of the roles the ARIA-in-HTML specification permits for that host element. Reported at CANTTELL rather than FAIL: ARIA-in-HTML's permitted-roles table is an author conformance requirement with no ACT rule and no WCAG mapping in any source. The role the author asked for is still the role assistive technology exposes, so whether the combination harms anyone depends on the widget, not on the table.
|
|
266
258
|
|
|
267
259
|
### `aria-braille-equivalent`
|
|
268
260
|
|
|
269
261
|
**aria-braillelabel/aria-brailleroledescription must have a non-braille equivalent**
|
|
270
262
|
|
|
271
|
-
automatic · WCAG 4.1.2 (A) · confidence high · default severity
|
|
263
|
+
automatic · WCAG 4.1.2 (A) · confidence high · default severity moderate
|
|
272
264
|
|
|
273
265
|
Checks that elements using aria-braillelabel also have a regular accessible name, and elements using aria-brailleroledescription also have aria-roledescription.
|
|
274
266
|
|
|
@@ -281,7 +273,7 @@ Per the ARIA specification, `aria-braillelabel` is a Braille-specific SUPPLEMENT
|
|
|
281
273
|
- a non-empty accessible name from a non-braille mechanism, if it declares `aria-braillelabel`;
|
|
282
274
|
- a non-empty `aria-roledescription`, if it declares `aria-brailleroledescription`.
|
|
283
275
|
|
|
284
|
-
Using either braille-specific attribute as the ONLY naming mechanism leaves non-braille assistive technology (most screen readers, voice control, etc.) with no accessible name/role description at all.
|
|
276
|
+
Using either braille-specific attribute as the ONLY naming mechanism leaves non-braille assistive technology (most screen readers, voice control, etc.) with no accessible name/role description at all. Reported at CANTTELL rather than FAIL: aria-brailleroledescription without aria-roledescription reaches no user at all, and a missing accessible name is the naming rules' decision for the roles that require one. The braille attribute being unpaired is worth surfacing, but it is not itself a criterion failing.
|
|
285
277
|
|
|
286
278
|
### `aria-checked-state-mismatch`
|
|
287
279
|
|
|
@@ -299,13 +291,13 @@ Flags a native <input type="checkbox">/<input type="radio"> whose ex
|
|
|
299
291
|
|
|
300
292
|
**aria-errormessage requires aria-invalid to be set to a non-false value**
|
|
301
293
|
|
|
302
|
-
automatic · WCAG 4.1.2 (A) · confidence high · default severity
|
|
294
|
+
automatic · WCAG 4.1.2 (A) · confidence high · default severity moderate
|
|
303
295
|
|
|
304
296
|
Checks that elements with aria-errormessage also have aria-invalid set to "true", "grammar", or "spelling"; otherwise the error message is dropped from the accessibility tree.
|
|
305
297
|
|
|
306
298
|
**Applies to.** Elements with a non-empty `aria-errormessage` attribute.
|
|
307
299
|
|
|
308
|
-
**Expectation.** Per the ARIA specification, `aria-errormessage` is only exposed to assistive technology when `aria-invalid` is present with a value other than `"false"` (i.e. `"true"`, `"grammar"`, or `"spelling"`). An element with `aria-errormessage` but `aria-invalid` absent or `"false"` silently drops the error message from the accessibility tree, authors almost always intend it to be exposed.
|
|
300
|
+
**Expectation.** Per the ARIA specification, `aria-errormessage` is only exposed to assistive technology when `aria-invalid` is present with a value other than `"false"` (i.e. `"true"`, `"grammar"`, or `"spelling"`). An element with `aria-errormessage` but `aria-invalid` absent or `"false"` silently drops the error message from the accessibility tree, authors almost always intend it to be exposed. Reported at CANTTELL rather than FAIL: aria-errormessage is only exposed once aria-invalid is set, so the reference is currently inert. Whether that costs the user anything depends on whether the message is conveyed some other way (visible text next to the field, aria-describedby), which static markup does not settle.
|
|
309
301
|
|
|
310
302
|
### `aria-deprecated-role`
|
|
311
303
|
|
|
@@ -354,6 +346,8 @@ No element with aria-hidden="true" may itself be focusable, and no focusable ele
|
|
|
354
346
|
|
|
355
347
|
- Focusability is computed via ctx.helpers.getFocusableInfo (native + tabindex + contenteditable).
|
|
356
348
|
- Elements that are not rendered (e.g., display:none, visibility:hidden, [hidden]) are excluded.
|
|
349
|
+
- Disabled controls are not focusable, including those disabled by an ancestor <fieldset disabled> (matched with :disabled).
|
|
350
|
+
- <area href> has no box of its own, so it is judged by the <img usemap> that uses its <map>: it counts as focusable when such an image is rendered and not inert.
|
|
357
351
|
- Elements hidden via CSS in ways that still allow keyboard focus (e.g., opacity:0, off-screen, clip) remain in-scope and will be flagged when focusable.
|
|
358
352
|
|
|
359
353
|
### `aria-prohibited-attr`
|
|
@@ -364,7 +358,7 @@ automatic · WCAG 4.1.2 (A) · confidence high · default severity moderate
|
|
|
364
358
|
|
|
365
359
|
Checks that aria-label/aria-labelledby are not present on WAI-ARIA roles whose specification explicitly prohibits ARIA naming (e.g. generic, emphasis, strong, paragraph).
|
|
366
360
|
|
|
367
|
-
**Applies to.** Applies to (a) elements whose explicit, valid role is one of the ARIA 1.2 roles with a documented "Prohibited ARIA States and Properties" list for naming attributes (pure text-semantics / non-naming structural roles: caption, code, deletion, emphasis, generic, insertion, mark, none, paragraph, presentation, strong, subscript, suggestion, superscript, time), and (b) elements with no role at all: a curated set of native HTML tags verified to carry no implicit role (see ROLELESS_NATIVE_TAGS below), or any autonomous custom element (a hyphenated, author-defined tag per the Custom Elements spec; see isRolelessCustomElementTag below). In both cases, only elements that also carry aria-label or aria-labelledby.
|
|
361
|
+
**Applies to.** Applies to (a) elements whose explicit, valid role is one of the ARIA 1.2 roles with a documented "Prohibited ARIA States and Properties" list for naming attributes (pure text-semantics / non-naming structural roles: caption, code, deletion, emphasis, generic, insertion, mark, none, paragraph, presentation, strong, subscript, suggestion, superscript, time), plus a native <caption> with no valid explicit role, whose implicit role is caption, and (b) elements with no role at all: a curated set of native HTML tags verified to carry no implicit role (see ROLELESS_NATIVE_TAGS below), or any autonomous custom element (a hyphenated, author-defined tag per the Custom Elements spec; see isRolelessCustomElementTag below). In both cases, only elements that also carry aria-label or aria-labelledby.
|
|
368
362
|
|
|
369
363
|
**Expectation.** Prohibited attributes must not be present on (a); for (b), the naming attribute is at best unreliable (nothing accessible-name-aware to hang it off) and at worst silently ignored by assistive technology. See the roleless-branch implementation note below for the confidence split this produces.
|
|
370
364
|
|
|
@@ -372,7 +366,7 @@ Checks that aria-label/aria-labelledby are not present on WAI-ARIA roles whose s
|
|
|
372
366
|
|
|
373
367
|
**Container roles must not own a child with a disallowed role**
|
|
374
368
|
|
|
375
|
-
automatic · WCAG
|
|
369
|
+
automatic · WCAG 1.3.1 (A) · confidence medium · default severity moderate
|
|
376
370
|
|
|
377
371
|
Checks that every accessible-tree-owned child of a container role (list, listbox, menu, menubar, radiogroup, rowgroup, table, grid, treegrid, tablist, tree, row) has one of that role's allowed owned roles.
|
|
378
372
|
|
|
@@ -388,27 +382,32 @@ automatic · WCAG 4.1.2 (A) · confidence high · default severity serious
|
|
|
388
382
|
|
|
389
383
|
Checks that elements with an explicit role carry every unambiguous, context-independent required aria-* state/property for that role (e.g. role="checkbox" must have aria-checked).
|
|
390
384
|
|
|
391
|
-
**Applies to.** Applies to elements with an explicit, valid, non-abstract role that is also one of the small set of roles with a documented, context- independent required state/property (checkbox, combobox, heading, menuitemcheckbox, menuitemradio, meter, radio, scrollbar, separator, slider, switch) -- except when that explicit role is identical to the element's own native/implicit role (ACT 4e8ab6: e.g. <input type="checkbox" role="checkbox">, which is exempt because the native control's own state exposure already covers it; no aria-checked is required. helpers.aria.getNativeRoleForElement resolves this).
|
|
385
|
+
**Applies to.** Applies to elements with an explicit, valid, non-abstract role that is also one of the small set of roles with a documented, context- independent required state/property (checkbox, combobox, heading, menuitemcheckbox, menuitemradio, meter, radio, scrollbar, separator, slider, switch) -- except when that explicit role is identical to the element's own native/implicit role (ACT 4e8ab6: e.g. <input type="checkbox" role="checkbox">, which is exempt because the native control's own state exposure already covers it; no aria-checked is required. helpers.aria.getNativeRoleForElement resolves this). A native <input type="checkbox"> or <input type="radio"> with another checkable role (switch, menuitemcheckbox, menuitemradio, or checkbox on a radio and the reverse) is in scope, but its aria-checked counts as supplied: the browser exposes the input's own checked state (HTML-AAM), so it passes without the attribute.
|
|
386
|
+
|
|
387
|
+
**Expectation.**
|
|
388
|
+
|
|
389
|
+
Every required state/property for that role is present and non-empty. Graded by whether ARIA supplies a stand-in for the missing attribute:
|
|
392
390
|
|
|
393
|
-
|
|
391
|
+
- FAIL where it does not, so the state is simply not exposed (aria-checked on checkbox/radio/switch/menuitemcheckbox/menuitemradio, aria-valuenow on slider/scrollbar/meter and on a focusable separator).
|
|
392
|
+
- CANTTELL where ARIA defines an implicit value the role falls back to (aria-expanded on combobox, aria-level on heading), so the role still exposes a value and only the author knows whether it is the right one.
|
|
394
393
|
|
|
395
394
|
### `aria-required-children`
|
|
396
395
|
|
|
397
396
|
**Container roles must own at least one required child role**
|
|
398
397
|
|
|
399
|
-
automatic · WCAG
|
|
398
|
+
automatic · WCAG 1.3.1 (A) · confidence medium · default severity moderate
|
|
400
399
|
|
|
401
400
|
Checks that container roles with a documented "required owned elements" entry (list, listbox, menu, radiogroup, table, grid, tablist, tree, row, ...) contain at least one descendant or aria-owns-referenced element with an acceptable owned role.
|
|
402
401
|
|
|
403
402
|
**Applies to.** Applies to elements with an explicit, valid, non-abstract role that is also one of the container roles with a documented "required owned elements" entry (list, listbox, menu, menubar, radiogroup, rowgroup, table, grid, treegrid, tablist, tree, row).
|
|
404
403
|
|
|
405
|
-
**Expectation.** At least one descendant, or one aria-owns-referenced element, has one of the acceptable owned roles for that container role.
|
|
404
|
+
**Expectation.** At least one descendant, or one aria-owns-referenced element, has one of the acceptable owned roles for that container role. Reported at CANTTELL, never FAIL: this rule asks only whether the required content is PRESENT, and a container that owns nothing conveys nothing false -- an empty role="list" is announced as a list with no items, which is what it is. Whether the content a container does own is VALID is aria-prohibited-children's decision, and that rule still fails, so a genuinely misdescribed structure (a role="button" among list items, a tablist of plain buttons) is caught with the same strength as before. The native-HTML equivalents already work this way: nothing in this ruleset fails an empty <ul>, and list-children-valid judges only the children that exist.
|
|
406
405
|
|
|
407
406
|
### `aria-required-parent`
|
|
408
407
|
|
|
409
408
|
**Roles requiring a specific context role must be in that context**
|
|
410
409
|
|
|
411
|
-
automatic · WCAG
|
|
410
|
+
automatic · WCAG 1.3.1 (A) · confidence medium · default severity moderate
|
|
412
411
|
|
|
413
412
|
Checks that roles with a documented "required context role" entry (listitem, option, tab, treeitem, row, cell, ...) have an ancestor or aria-owns owner with an acceptable context role.
|
|
414
413
|
|
|
@@ -426,7 +425,7 @@ Checks that the ARIA roles WAI-ARIA requires an accessible name for expose a non
|
|
|
426
425
|
|
|
427
426
|
**Applies to.** Applies to elements whose role attribute is exactly one of grid, meter, progressbar, radiogroup or tree, and that are included in the accessibility tree. Membership is decided by WAI-ARIA's own "Accessible Name Required: True" characteristic, not by whether a role merely permits a name: tablist, toolbar, menu, menubar and scrollbar are name-from-author roles the spec does not require a name for, and are out of scope. meter and progressbar are also covered by meter-name-present and progressbar-name-present, which map to SC 1.1.1; this rule is what gives those two roles their 4.1.2 coverage.
|
|
428
427
|
|
|
429
|
-
**Expectation.** The element has a non-empty aria-label, an aria-labelledby that resolves to non-empty text, or a non-empty title. Every role in the set is name-from-author-only, so descendant text is not accepted: a labelled child inside a composite widget would otherwise pass the container that has no name of its own.
|
|
428
|
+
**Expectation.** The element has a non-empty aria-label, an aria-labelledby that resolves to non-empty text, or a non-empty title. Every role in the set is name-from-author-only, so descendant text is not accepted: a labelled child inside a composite widget would otherwise pass the container that has no name of its own. The name the HTML host element gives itself counts too, since the browser still computes it under the role: the first child <legend> of a <fieldset>, the first child <caption> of a <table>, and an associated <label> on a labelable element such as <progress> or <meter>.
|
|
430
429
|
|
|
431
430
|
### `aria-roles-valid`
|
|
432
431
|
|
|
@@ -438,7 +437,12 @@ Checks that an explicit role="" attribute resolves to a real, non-abstract WAI-A
|
|
|
438
437
|
|
|
439
438
|
**Applies to.** Applies to any element with a non-empty role="" attribute in the composed DOM.
|
|
440
439
|
|
|
441
|
-
**Expectation.**
|
|
440
|
+
**Expectation.**
|
|
441
|
+
|
|
442
|
+
At least one role token names a concrete, non-abstract ARIA role. Graded by what the element falls back to when none does:
|
|
443
|
+
|
|
444
|
+
- FAIL on a roleless host (div, span, custom element), which is left exposed as generic, so the role the author meant reaches no one.
|
|
445
|
+
- CANTTELL where the element has a native role (a <button>, <nav>, <a href>), which the accessibility tree keeps using. ACT 674b10 lists 4.1.2 as a secondary requirement only, "satisfied through the implicit role," so the bad token is worth reporting but is not itself the criterion failing.
|
|
442
446
|
|
|
443
447
|
### `aria-text`
|
|
444
448
|
|
|
@@ -462,7 +466,7 @@ Checks that every aria-* attribute name present in the DOM is a real attribute d
|
|
|
462
466
|
|
|
463
467
|
**Applies to.** Applies to any element in the composed DOM that carries at least one attribute whose name starts with "aria-".
|
|
464
468
|
|
|
465
|
-
**Expectation.** Each aria-* attribute name is a real attribute defined by the WAI-ARIA specification (catches typos / made-up attribute names, which are silently ignored by assistive technology and therefore a real, deterministic defect).
|
|
469
|
+
**Expectation.** Each aria-* attribute name is a real attribute defined by the WAI-ARIA specification (catches typos / made-up attribute names, which are silently ignored by assistive technology and therefore a real, deterministic defect). Reported at CANTTELL rather than FAIL: an aria-* attribute the spec does not define is inert, so nothing about the element's exposed name, role or value changes because it is there. Where the author meant a real attribute and the element ends up without a name, that absence is the naming rules' decision, not this one's. ACT 5f99a7 maps 1.3.1/4.1.2 as secondary requirements, "less strict" than the rule itself.
|
|
466
470
|
|
|
467
471
|
### `aria-valid-attr-value`
|
|
468
472
|
|
|
@@ -474,7 +478,7 @@ Checks that every recognized aria-* attribute has a value conforming to its WAI-
|
|
|
474
478
|
|
|
475
479
|
**Applies to.** Applies to any element carrying at least one recognized aria-* attribute (unrecognized attribute names are aria-valid-attr's concern, not evaluated here).
|
|
476
480
|
|
|
477
|
-
**Expectation.** Each attribute's value conforms to its WAI-ARIA-declared value type: boolean ("true"/"false"), tristate ("true"/"false"/"mixed"), a token from a fixed enumerated set, an integer, a real number, or an ID reference (list) that resolves to an existing element in the document. Per ACT 6a7281's own applicability ("any state or property that is NOT empty"), an explicitly empty value, including a bare boolean-style attribute with no "=value" at all, e.g. `aria-checked` alone, is out of scope for every value type, not a violation: a common, deliberate pattern in templated markup (e.g. React conditionally rendering `aria-describedby={hasError ? errorId : ''}`).
|
|
481
|
+
**Expectation.** Each attribute's value conforms to its WAI-ARIA-declared value type: boolean ("true"/"false"), tristate ("true"/"false"/"mixed"), a token from a fixed enumerated set, an integer (within the range WAI-ARIA sets for it), a real number, or an ID reference (list) that resolves to an existing element in the document. Per ACT 6a7281's own applicability ("any state or property that is NOT empty"), an explicitly empty value, including a bare boolean-style attribute with no "=value" at all, e.g. `aria-checked` alone, is out of scope for every value type, not a violation: a common, deliberate pattern in templated markup (e.g. React conditionally rendering `aria-describedby={hasError ? errorId : ''}`).
|
|
478
482
|
|
|
479
483
|
### `autocomplete-valid`
|
|
480
484
|
|
|
@@ -484,9 +488,9 @@ automatic · WCAG 1.3.5 (AA) · confidence high · default severity moderate
|
|
|
484
488
|
|
|
485
489
|
Checks that a non-empty autocomplete attribute is "on"/"off" or a well-formed autofill detail token list.
|
|
486
490
|
|
|
487
|
-
**Applies to.** Applies to form controls (input, select, textarea) with a non-empty autocomplete attribute.
|
|
491
|
+
**Applies to.** Applies to form controls (input, select, textarea) with a non-empty autocomplete attribute. Disabled controls (the disabled attribute, including a control disabled by a disabled fieldset ancestor, or aria-disabled="true") and input types with a fixed value are exempt, as in ACT 73f2c2.
|
|
488
492
|
|
|
489
|
-
**Expectation.** The value is "on"/"off" alone, or a well-formed autofill detail token list: an optional "section-*" token, then an optional "shipping"/"billing" token, then an optional contact-modality token (home/work/mobile/fax/pager
|
|
493
|
+
**Expectation.** The value is "on"/"off" alone, or a well-formed autofill detail token list: an optional "section-*" token, then an optional "shipping"/"billing" token, then an optional contact-modality token (home/work/mobile/fax/pager), then exactly one recognized field-name token (name, email, street-address, cc-number, tel, ...), optionally followed by "webauthn". The field name must also suit the control: the HTML Standard gives each field name a control group, and each group is allowed only on some input types (street-address only on textarea or select; email only on text, search or email inputs; and so on). A malformed or unsuitable value means the field is not reliably identified for assistive technology that relies on autocomplete to describe the expected input purpose.
|
|
490
494
|
|
|
491
495
|
### `avoid-inline-spacing`
|
|
492
496
|
|
|
@@ -522,7 +526,7 @@ Checks that buttons expose a non-empty accessible name.
|
|
|
522
526
|
|
|
523
527
|
**Applies to.** Applies to <button>, <input type="button">, <input type="submit">, <input type="reset"> and elements with role="button", where the element is included in the accessibility tree. role="presentation"/"none" takes an element out of scope unless a global ARIA attribute or focusability restores its role, per presentational roles conflict resolution.
|
|
524
528
|
|
|
525
|
-
**Expectation.** The element has a non-empty accessible name. A programmatic name is taken first (aria-labelledby, aria-label, an associated <label>, title). Failing that, an <input> button falls back to its value attribute, and type="submit"/type="reset" fall back to the user agent's own "Submit"/"Reset" default, which is why those two are never nameless. Failing both, a button whose role is name-from-content falls back to its subtree text, counting each descendant's own name (an <img alt>, aria-label or title) rather than only text nodes.
|
|
529
|
+
**Expectation.** The element has a non-empty accessible name. A programmatic name is taken first (aria-labelledby, aria-label, an associated <label>, title). Failing that, an <input> button falls back to its value attribute, and type="submit"/type="reset" fall back to the user agent's own "Submit"/"Reset" default, which is why those two are never nameless. Failing both, a button whose role is name-from-content falls back to its subtree text, counting each descendant's own name (an <img alt>, aria-label, an <svg>'s <title> child, or title) rather than only text nodes.
|
|
526
530
|
|
|
527
531
|
### `bypass-blocks-present`
|
|
528
532
|
|
|
@@ -546,7 +550,12 @@ Checks that <canvas> elements provide a text alternative via fallback cont
|
|
|
546
550
|
|
|
547
551
|
**Applies to.** Applies to <canvas> elements included in the accessibility tree. Hidden elements are excluded whether or not they are focusable.
|
|
548
552
|
|
|
549
|
-
**Expectation.**
|
|
553
|
+
**Expectation.**
|
|
554
|
+
|
|
555
|
+
Each applicable <canvas> provides a text alternative via fallback content or an accessible name, with two exceptions:
|
|
556
|
+
|
|
557
|
+
- role="img" (first role token) makes the canvas's children presentational and its name comes from the author only, so fallback content does not count: aria-labelledby, aria-label or title must name it.
|
|
558
|
+
- role="none"/"presentation" marks the canvas decorative, and it passes. The role is ignored (presentational role conflict) when the canvas is focusable or carries aria-label/aria-labelledby, and the canvas is then judged like any other.
|
|
550
559
|
|
|
551
560
|
### `canvas-text-alternative-quality`
|
|
552
561
|
|
|
@@ -570,7 +579,7 @@ Checks that elements with role="combobox" expose a non-empty accessible name.
|
|
|
570
579
|
|
|
571
580
|
**Applies to.** Applies to elements carrying role="combobox" (the attribute must name that role alone, not a fallback list) that are included in the accessibility tree. An element with the matching implicit role but no role attribute is out of scope.
|
|
572
581
|
|
|
573
|
-
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="combobox" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom combobox widget is not reliably exposed as its name. On a labelable element (<input role="combobox">) an associated <label> counts as well.
|
|
582
|
+
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="combobox" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom combobox widget is not reliably exposed as its name. On a labelable element (<input role="combobox">) an associated <label> counts as well. On a text-like <input> or a <textarea>, the placeholder counts last (HTML-AAM's final name source): a placeholder-only label is a 3.3.2 question, not a missing name.
|
|
574
583
|
|
|
575
584
|
### `contrast-computable`
|
|
576
585
|
|
|
@@ -614,11 +623,11 @@ Checks that visible text has a contrast ratio of at least 4.5:1 (normal) or 3:1
|
|
|
614
623
|
|
|
615
624
|
manual · WCAG 2.4.7 (AA) · confidence medium · default severity serious
|
|
616
625
|
|
|
617
|
-
Flags elements in the tab order whose focus outline is removed by a :focus/:focus-visible rule with no replacement indicator (border, box-shadow, background, ...) in any
|
|
626
|
+
Flags elements in the tab order whose focus outline is removed, by a :focus/:focus-visible rule or by a rule with no state such as a { outline: none }, with no replacement indicator (border, box-shadow, background, ...) in any focus rule matching them.
|
|
618
627
|
|
|
619
|
-
**Applies to.** Elements in sequential focus navigation (tabbable and rendered) on a page whose accessible stylesheets contain at least one
|
|
628
|
+
**Applies to.** Elements in sequential focus navigation (tabbable and rendered) on a page whose accessible stylesheets contain at least one rule that removes the outline. With no such rule anywhere, every element keeps the user agent's own indicator and there is nothing to check.
|
|
620
629
|
|
|
621
|
-
**Expectation.** No element is matched by a
|
|
630
|
+
**Expectation.** No element is matched by a rule that removes the outline (`outline: none`, `outline: 0`, `outline-color: transparent`, ...) unless some focus rule matching it draws a replacement: a border, box-shadow, background, color change, a positive outline of its own, or a `::before`/`::after` decoration. The removing rule is either a `:focus`/`:focus-visible` rule, or a rule with no state at all (`a { outline: none }`, `* { outline: 0 }`): an author declaration outranks the user agent's focus outline whatever its specificity, so it removes the indicator in the focused state too (WCAG F78).
|
|
622
631
|
|
|
623
632
|
### `css-hidden-focus`
|
|
624
633
|
|
|
@@ -632,11 +641,12 @@ Checks that keyboard-focusable elements are not visually hidden by CSS technique
|
|
|
632
641
|
|
|
633
642
|
**Expectation.**
|
|
634
643
|
|
|
635
|
-
No element should be tabbable while visually hidden (e.g., opacity:0, clipped, off-screen). Notes:
|
|
644
|
+
No element should be tabbable while visually hidden (e.g., opacity:0, clipped, off-screen). An element that CSS brings back into view when it takes focus is not hidden while focused, and is not flagged: the usual skip-link pattern (`.skip { position: absolute; left: -9999px } .skip:focus { left: 0 }`), or a hiding rule that stops applying on focus (`.visually-hidden-focusable:not(:focus) { clip: rect(0 0 0 0) }`). Notes:
|
|
636
645
|
|
|
637
646
|
- This rule intentionally targets CSS techniques that *can* keep an element focusable.
|
|
638
647
|
- Elements removed from rendering (display:none, visibility:hidden, [hidden]) are excluded.
|
|
639
648
|
- The rule uses deterministic heuristics (computed style parsing) and does not rely on layout geometry.
|
|
649
|
+
- The focused style is worked out from the stylesheets, not by focusing the element: a DOM emulator does not restyle `:focus`. Rules whose subject carries `:focus`, `:focus-visible` or `:focus-within` (or an ancestor carries `:focus-within`) are laid over the computed style in document order; a rule written `:not(:focus)` / `:not(:focus-within)` / `:not(:focus-visible)` has its declarations reset to their initial values. Inline style outranks a stylesheet rule unless the rule is `!important`. The overlay ignores specificity among the focus rules, and cross-origin stylesheets cannot be read, so their focus rules are not seen.
|
|
640
650
|
|
|
641
651
|
### `css-orientation-lock`
|
|
642
652
|
|
|
@@ -644,11 +654,11 @@ No element should be tabbable while visually hidden (e.g., opacity:0, clipped, o
|
|
|
644
654
|
|
|
645
655
|
automatic · WCAG 1.3.4 (AA) · confidence high · default severity serious
|
|
646
656
|
|
|
647
|
-
Checks that no @media (orientation: portrait|landscape) rule sets a transform: rotate(...) on the page, a known technique for defeating device orientation.
|
|
657
|
+
Checks that no @media (orientation: portrait|landscape) rule sets a transform: rotate(...) on the page, a known technique for defeating device orientation, and asks about any such rule that hides the page's main content.
|
|
648
658
|
|
|
649
659
|
**Applies to.** Any accessible (same-document, non-cross-origin) stylesheet, inline `<style>` blocks and same-origin `<link>` stylesheets already loaded into `document.styleSheets`.
|
|
650
660
|
|
|
651
|
-
**Expectation.** No `@media (orientation: portrait)` or `@media (orientation: landscape)` block sets a `transform`/`-webkit-transform`/`rotate` rotation of approximately 90 degrees (mod 180, i.e. ~90 or ~270), the well-known technique for visually forcing one orientation regardless of the device's actual orientation, which defeats WCAG 1.3.4's requirement that content not restrict its view to a single display orientation unless that orientation is essential.
|
|
661
|
+
**Expectation.** No `@media (orientation: portrait)` or `@media (orientation: landscape)` block sets a `transform`/`-webkit-transform`/`rotate` rotation of approximately 90 degrees (mod 180, i.e. ~90 or ~270), the well-known technique for visually forcing one orientation regardless of the device's actual orientation, which defeats WCAG 1.3.4's requirement that content not restrict its view to a single display orientation unless that orientation is essential. Such a rotation fails. A second shape is asked about (cantTell): an orientation media block that hides the page's content with `display: none` or `visibility: hidden`, the usual form of WCAG F100 (content replaced by a "rotate your device" message in one orientation). The hidden element counts as the page's content when it is `html` or `body`, the `main` landmark, an ancestor of it, or, on a page without a main landmark, an element holding at least half of the body's text. Whether the orientation is essential, and whether the page stays usable, is left to a person.
|
|
652
662
|
|
|
653
663
|
### `definition-list-children-valid`
|
|
654
664
|
|
|
@@ -658,21 +668,21 @@ automatic · WCAG 1.3.1 (A) · confidence high · default severity serious
|
|
|
658
668
|
|
|
659
669
|
Checks that <dl> elements only directly contain <dt>/<dd> groups (optionally wrapped in one <div>), <script>, <template>, or <style>.
|
|
660
670
|
|
|
661
|
-
**Applies to.** Applies to <dl> elements that have at least one direct element child.
|
|
671
|
+
**Applies to.** Applies to <dl> elements that have at least one direct element child or non-whitespace text directly inside them.
|
|
662
672
|
|
|
663
|
-
**Expectation.** Every direct element child is <dt>, <dd>, <script>, <template>, <style>, or a <div> whose own children are drawn from that same set (a single level of wrapping div is allowed, matching how authors commonly group dt/dd pairs). If the flattened set contains any <dt> or <dd> at all, it must contain BOTH (an unbalanced dt-without-dd or dd-without-dt is invalid). A flattened set with neither is vacuously fine, not a violation (see implementation-notes). Any other direct or wrapped child breaks the description-list semantics assistive technologies rely on.
|
|
673
|
+
**Expectation.** Every direct element child is <dt>, <dd>, <script>, <template>, <style>, or a <div> whose own children are drawn from that same set (a single level of wrapping div is allowed, matching how authors commonly group dt/dd pairs). Non-whitespace text directly inside the <dl> or a wrapping <div> is an invalid child too. If the flattened set contains any <dt> or <dd> at all, it must contain BOTH (an unbalanced dt-without-dd or dd-without-dt is invalid), and read in order it must be groups of one or more <dt> followed by one or more <dd>: a <dd> before the first <dt> has no term, and a <dt> after the last <dd> has no definition. A flattened set with neither is vacuously fine, not a violation (see implementation-notes). Any other direct or wrapped child breaks the description-list semantics assistive technologies rely on.
|
|
664
674
|
|
|
665
675
|
### `deprecated-elements-not-used`
|
|
666
676
|
|
|
667
|
-
**
|
|
677
|
+
**Scrolling <marquee> content must be possible to pause, stop, or hide**
|
|
668
678
|
|
|
669
679
|
automatic · WCAG 2.2.2 (A) · confidence high · default severity serious
|
|
670
680
|
|
|
671
|
-
|
|
681
|
+
Asks, for each obsolete <marquee> element, whether the page offers a way to pause, stop, or hide its auto-scrolling content, since the element itself has none.
|
|
672
682
|
|
|
673
|
-
**Applies to.** Applies to any
|
|
683
|
+
**Applies to.** Applies to any scan scope; whether it contains a <marquee> element is always an answerable question. <marquee> is an obsolete, non-standard HTML element that browsers still render as auto-scrolling text, with no built-in user mechanism to pause, stop, or hide it.
|
|
674
684
|
|
|
675
|
-
**Expectation.**
|
|
685
|
+
**Expectation.** Each <marquee> is reported as cantTell: the scrolling itself is certain, but a page can offer its own pause or stop control (a button calling the element's stop() method, for example), and then failure F16 does not apply. Whether such a control exists is for a person to check. A scope with no <marquee> passes; there is no separate not-applicable case.
|
|
676
686
|
|
|
677
687
|
### `dialog-name-present`
|
|
678
688
|
|
|
@@ -680,9 +690,9 @@ Checks that deprecated, non-standard HTML elements whose blinking/scrolling cont
|
|
|
680
690
|
|
|
681
691
|
automatic · WCAG 4.1.2 (A) · confidence high · default severity serious
|
|
682
692
|
|
|
683
|
-
Checks that elements with role="dialog" or role="alertdialog" expose a non-empty accessible name.
|
|
693
|
+
Checks that dialogs (elements with role="dialog" or role="alertdialog", and native <dialog> elements) expose a non-empty accessible name.
|
|
684
694
|
|
|
685
|
-
**Applies to.** Applies to elements
|
|
695
|
+
**Applies to.** Applies to elements included in the accessibility tree whose role is dialog or alertdialog: an element whose role attribute resolves to one of those roles (the first token that names a concrete ARIA role wins, so role="alertdialog dialog" is an alertdialog), and a native <dialog> whose role attribute is absent or names no concrete role (its implicit role is dialog). A closed <dialog> is hidden and so not in scope.
|
|
686
696
|
|
|
687
697
|
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. Both roles are name-from-author-only, so the heading or body text inside the dialog is not accepted as its name unless aria-labelledby points at it.
|
|
688
698
|
|
|
@@ -708,7 +718,7 @@ Checks that every non-empty id attribute value is unique within its own document
|
|
|
708
718
|
|
|
709
719
|
**Applies to.** Applies to any element carrying a non-empty id attribute. Visibility is irrelevant. A duplicate id breaks the same lookups whether the element renders or not, which is why ACT 3ea0c8 evaluates hidden elements too.
|
|
710
720
|
|
|
711
|
-
**Expectation.** No other element in the same tree carries the same id value. Ids are scoped per document tree and per shadow tree, so the same id inside two different shadow roots is not a duplicate.
|
|
721
|
+
**Expectation.** No other element in the same tree carries the same id value, compared exactly as written (id="a " and id="a" are different ids). Ids are scoped per document tree and per shadow tree, so the same id inside two different shadow roots is not a duplicate.
|
|
712
722
|
|
|
713
723
|
### `duplicate-id-aria`
|
|
714
724
|
|
|
@@ -795,7 +805,7 @@ Flags elements with tabindex >= 0 whose explicit role is a non-interactive st
|
|
|
795
805
|
|
|
796
806
|
manual · WCAG 2.4.6 (AA) · confidence medium · default severity minor
|
|
797
807
|
|
|
798
|
-
Flags a visible form-field label that is a placeholder ("Label", "Field"), or that repeats another field's label with no visible context (heading, legend, or row) telling the two apart.
|
|
808
|
+
Flags a visible form-field label that is a placeholder ("Label", "Field"), or that repeats another field's label with no visible context (heading, legend, or row) telling the two apart. English placeholders are always recognized, and German, Spanish, French or Japanese ones when the field is in that language.
|
|
799
809
|
|
|
800
810
|
**Applies to.** Visible form fields: native `input` (excluding hidden and the button-like types), `select`, `textarea`, or an element with one of the ARIA widget roles ACT cc0f0a lists (checkbox, combobox, listbox, menuitemcheckbox, menuitemradio, radio, searchbox, slider, spinbutton, switch, textbox) that carry a visible programmatic label: a `<label>` association, or the elements `aria-labelledby` points at. A field named only by `aria-label`/`title` has no visible label to judge and is out of scope here (its labelling mechanism is `form-control-programmatic-label-quality`'s concern, its presence `form-control-programmatic-label-present`'s).
|
|
801
811
|
|
|
@@ -868,7 +878,7 @@ manual · no formal WCAG SC mapping · confidence medium · default severity min
|
|
|
868
878
|
|
|
869
879
|
Checks that heading levels increase by at most one at a time in document order.
|
|
870
880
|
|
|
871
|
-
**Applies to.** Applies whenever the page contains two or more heading elements (native <h1>-<h6>, or explicit role="heading" with aria-level; default level 2 per the ARIA spec when aria-level is absent/invalid).
|
|
881
|
+
**Applies to.** Applies whenever the page contains two or more heading elements (native <h1>-<h6>, or explicit role="heading" with aria-level; default level 2 per the ARIA spec when aria-level is absent/invalid). A native <hx> with a valid aria-level (a positive integer) takes that level, as browsers expose it; otherwise it takes its tag level.
|
|
872
882
|
|
|
873
883
|
**Expectation.** In document order, each heading's level is no more than one greater than the highest heading level seen so far. Jumping deeper by more than one level (e.g. an <h1> followed directly by an <h3>, skipping <h2>) breaks the document outline assistive technology users rely on when navigating by heading. Going back to a shallower level at any point is always fine.
|
|
874
884
|
|
|
@@ -878,7 +888,7 @@ Checks that heading levels increase by at most one at a time in document order.
|
|
|
878
888
|
|
|
879
889
|
manual · WCAG 2.4.6 (AA) · confidence medium · default severity minor
|
|
880
890
|
|
|
881
|
-
Flags headings whose accessible name is a placeholder rather than a description of the content that follows: a generic word ("Heading", "Untitled"), a numbered template slot ("Section 2"), a filename, or a URL.
|
|
891
|
+
Flags headings whose accessible name is a placeholder rather than a description of the content that follows: a generic word ("Heading", "Untitled"), a numbered template slot ("Section 2"), a filename, or a URL. English phrases are always recognized, and German, Spanish, French or Japanese ones when the heading is in that language.
|
|
882
892
|
|
|
883
893
|
**Applies to.** Elements with a heading role (native <h1>-<h6>, or any element with an explicit role="heading") that are included in the accessibility tree and have a non-empty accessible name. A heading with no name at all is `empty-heading`'s concern, not this rule's.
|
|
884
894
|
|
|
@@ -894,7 +904,7 @@ Checks that the default language of the page is programmatically declared.
|
|
|
894
904
|
|
|
895
905
|
**Applies to.** Applies to HTML documents with a root <html> element. The rule evaluates the document element only and does not iterate over child nodes. Non-HTML documents or documents without a document element are not applicable.
|
|
896
906
|
|
|
897
|
-
**Expectation.** The <html> element has a lang attribute. The lang attribute is not empty and
|
|
907
|
+
**Expectation.** The <html> element has a lang attribute. The lang attribute is not empty and its primary language subtag (the part before the first hyphen) is a registered language subtag, as ACT bf051a requires. A malformed later subtag (lang="fr-FR-!!") still identifies French, so it passes here; it is a markup validity error, not a missing language.
|
|
898
908
|
|
|
899
909
|
### `html-xml-lang-mismatch`
|
|
900
910
|
|
|
@@ -908,6 +918,18 @@ Checks that the <html> element's lang and xml:lang attributes declare the
|
|
|
908
918
|
|
|
909
919
|
**Expectation.** The primary language subtag (the part before the first "-") of lang and xml:lang match, case-insensitively. When both attributes are present but declare different languages, assistive technology and user agents may resolve the page's language inconsistently.
|
|
910
920
|
|
|
921
|
+
### `identical-iframes-same-purpose`
|
|
922
|
+
|
|
923
|
+
**Frames with the same name embed the same resource**
|
|
924
|
+
|
|
925
|
+
automatic · WCAG 4.1.2 (A) · confidence medium · default severity moderate
|
|
926
|
+
|
|
927
|
+
Checks that <iframe>/<frame> elements sharing an accessible name embed the same resource, since one name can only describe one resource.
|
|
928
|
+
|
|
929
|
+
**Applies to.** Applies to each set of two or more <iframe>/<frame> elements that are included in the accessibility tree and share the same non-empty accessible name, compared with whitespace collapsed. A frame named only by a mechanism that names nothing, or hidden from the accessibility tree, is not part of a set; a set needs two surviving members to exist at all.
|
|
930
|
+
|
|
931
|
+
**Expectation.** Every frame in a set resolves to the same resource. A shared name describes one resource, so two frames answering to it must embed the same one.
|
|
932
|
+
|
|
911
933
|
### `identical-links-same-purpose`
|
|
912
934
|
|
|
913
935
|
**Links with the same accessible name should lead to the same destination**
|
|
@@ -946,15 +968,15 @@ Checks that <iframe>/<frame> elements expose a non-empty accessible
|
|
|
946
968
|
|
|
947
969
|
### `iframe-title-unique`
|
|
948
970
|
|
|
949
|
-
**Frame
|
|
971
|
+
**Frame title uniqueness (deprecated)**
|
|
950
972
|
|
|
951
973
|
automatic · WCAG 4.1.2 (A) · confidence high · default severity moderate
|
|
952
974
|
|
|
953
|
-
|
|
975
|
+
Deprecated since 1.8.0 and always notApplicable: whether frames sharing a name embed the same resource is checked by identical-iframes-same-purpose.
|
|
954
976
|
|
|
955
|
-
**Applies to.**
|
|
977
|
+
**Applies to.** Nothing. The rule is deprecated and reports notApplicable on every page. Its id stays in the catalog, with meta.deprecated set and deprecation.replacedBy naming the successor, so a runOnly list, a stored baseline or an open Code Scanning alert that holds the id keeps resolving until the file is removed in 2.0.0 (docs/API_STABILITY.md, "Rule-ID deprecation policy").
|
|
956
978
|
|
|
957
|
-
**Expectation.**
|
|
979
|
+
**Expectation.** None. The check this rule used to make, that no two frames share a title attribute, is not a WCAG 4.1.2 requirement: the criterion asks that a frame's name be programmatically determinable, not unique, and ACT rule 4b1c6c accepts identical names on frames that embed equivalent resources. identical-iframes-same-purpose asks the question 4b1c6c does ask, of the computed accessible name, which for a frame is the title attribute unless aria-label or aria-labelledby overrides it.
|
|
958
980
|
|
|
959
981
|
### `image-redundant-alt`
|
|
960
982
|
|
|
@@ -976,7 +998,7 @@ manual · WCAG 1.1.1 (A) · confidence medium · default severity minor
|
|
|
976
998
|
|
|
977
999
|
Flags <img>, <canvas> and <svg> elements excluded from the accessibility tree (aria-hidden, role="none"/"presentation", empty alt, or an unlabeled svg/canvas) for human review that they are purely decorative.
|
|
978
1000
|
|
|
979
|
-
**Applies to.** Applies to visible <img>, <canvas> or <svg> elements excluded from the accessibility tree by any of: an aria-hidden ancestor-or-self, an explicit role="none"/"presentation" not overridden by focusability, an <img alt=""> (the native decorative marker, same focusability override), an unlabeled <svg> whose implicit role is graphics-document (no img/graphics-symbol role restatement, aria-name, <title>/<desc>, or focusability), or an unlabeled <canvas> with no explicit role at all. Per ACT e88epe, an element is skipped entirely when any ancestor already has an author-supplied name (aria-label, aria-labelledby, title, or an associated <label>), that ancestor's name is what matters, not this element's exclusion (the common real case: an icon-only button already named via aria-label).
|
|
1001
|
+
**Applies to.** Applies to visible <img>, <canvas> or <svg> elements excluded from the accessibility tree by any of: an aria-hidden ancestor-or-self, an explicit role="none"/"presentation" not overridden by focusability, an <img alt=""> (the native decorative marker, same focusability override; only a literally empty alt, so alt=" " is not one), an unlabeled <svg> whose implicit role is graphics-document (no img/graphics-symbol role restatement, aria-name, <title>/<desc>, or focusability), or an unlabeled <canvas> with no explicit role at all. Per ACT e88epe, an element is skipped entirely when any ancestor already has an author-supplied name (aria-label, aria-labelledby, title, or an associated <label>), that ancestor's name is what matters, not this element's exclusion (the common real case: an icon-only button already named via aria-label).
|
|
980
1002
|
|
|
981
1003
|
**Expectation.** Human review is required to confirm the excluded element is purely decorative and conveys no information a user would otherwise miss.
|
|
982
1004
|
|
|
@@ -1026,19 +1048,19 @@ Checks that <input type="image"> elements provide an alt attribute to supp
|
|
|
1026
1048
|
|
|
1027
1049
|
**Applies to.** Applies to <input type="image"> elements included in the accessibility tree.
|
|
1028
1050
|
|
|
1029
|
-
**Expectation.** Each applicable <input type="image"> element has
|
|
1051
|
+
**Expectation.** Each applicable <input type="image"> element has a non-empty accessible name from aria-labelledby, aria-label, alt or title. An empty name fails, whether alt is missing or empty (alt=""). An author-supplied name that equals a browser default for an image button ("Submit", "Submit Query") is not an empty name: it may describe the button, so it is reported as cantTell for a person to judge.
|
|
1030
1052
|
|
|
1031
1053
|
### `input-image-alt-quality`
|
|
1032
1054
|
|
|
1033
|
-
**<input type="image">
|
|
1055
|
+
**<input type="image"> text alternative must be appropriate (manual review)**
|
|
1034
1056
|
|
|
1035
1057
|
manual · WCAG 1.1.1 (A) · confidence medium · default severity minor
|
|
1036
1058
|
|
|
1037
|
-
Flags <input type="image"> elements with non-empty alt
|
|
1059
|
+
Flags <input type="image"> elements with a non-empty text alternative (alt, aria-label, aria-labelledby or title) for human review of appropriateness.
|
|
1038
1060
|
|
|
1039
|
-
**Applies to.** Applies to <input type="image"> elements whose alt
|
|
1061
|
+
**Applies to.** Applies to <input type="image"> elements that get a non-empty text alternative from any source: aria-labelledby (resolving to text), aria-label, alt or title. An element whose alt is present but empty is left to input-image-alt-decorative, which asks about that case. The element must be included in the accessibility tree, and role="presentation"/"none" takes it out of scope unless it is focusable, which restores its role.
|
|
1040
1062
|
|
|
1041
|
-
**Expectation.** Human review is required to confirm that the provided text alternative is accurate and appropriate.
|
|
1063
|
+
**Expectation.** Human review is required to confirm that the provided text alternative is accurate and appropriate. Each occurrence lists every source present (data.details.sources), so the reviewer checks each one.
|
|
1042
1064
|
|
|
1043
1065
|
### `label-in-name`
|
|
1044
1066
|
|
|
@@ -1048,9 +1070,9 @@ automatic · WCAG 2.5.3 (A) · confidence high · default severity serious
|
|
|
1048
1070
|
|
|
1049
1071
|
Checks that when a control has a visible text label, the accessible name contains that visible label text (WCAG 2.5.3).
|
|
1050
1072
|
|
|
1051
|
-
**Applies to.** Applies to controls
|
|
1073
|
+
**Applies to.** Applies to controls named by aria-label or aria-labelledby, are visually rendered, and have visible label text this engine can extract deterministically, from an associated <label>, from the control's own rendered text, or from the elements aria-labelledby points at. The candidates are <button>, <a href>, <summary>, non-hidden form controls, and the button, link, checkbox, radio, switch, searchbox, tab, menuitem, menuitemcheckbox, menuitemradio, option, treeitem and gridcell roles, minus anything hidden or disabled. aria-hidden is not excluded: it changes nothing about what is rendered on screen, which is what this SC is about. An aria-label that is empty once trimmed, or an aria-labelledby whose ids point at nothing or only at elements with no text, names nothing: the accessible name then comes from the next source (the content, a <label>, title...), as for a control without them, so the control is out of scope.
|
|
1052
1074
|
|
|
1053
|
-
**Expectation.** The accessible name contains the visible label's words, adjacent and in order. The comparison is over words rather than characters: parenthesised text is dropped, case is folded, text is
|
|
1075
|
+
**Expectation.** The accessible name contains the visible label's words, adjacent and in order. The comparison is over words rather than characters: parenthesised text is dropped, case is folded, text is NFKC-normalised, and every character that is not a letter, digit or combining mark becomes a separator, so punctuation and spacing differences never decide the outcome. Accents are not folded: "Déposer" stays one word, and a name that drops an accent ("Deposer") does not contain it. Four shapes markup cannot settle are reported as cantTell instead of fail: a word hyphenated differently in the two places; a visible word the author may have abbreviated, marked by its trailing period; visible text rendered through a known icon font (the DOM text is real words, but nothing readable actually renders); and a whole visible label of exactly one character that doesn't even appear inside the accessible name, which per ACT 2ee8b8 may be "non-text content" (e.g. "X" meaning "close") rather than literal text.
|
|
1054
1076
|
|
|
1055
1077
|
### `label-title-only`
|
|
1056
1078
|
|
|
@@ -1076,6 +1098,18 @@ Checks that the banner landmark (role="banner" or a non-nested <header>) i
|
|
|
1076
1098
|
|
|
1077
1099
|
**Expectation.** No banner candidate has an ancestor that is itself any landmark region. A banner nested inside another landmark is not a top-level, whole-page banner and confuses landmark-based navigation for assistive technology users.
|
|
1078
1100
|
|
|
1101
|
+
### `landmark-complementary-is-top-level`
|
|
1102
|
+
|
|
1103
|
+
**Complementary landmark must be top-level**
|
|
1104
|
+
|
|
1105
|
+
manual · no formal WCAG SC mapping · confidence medium · default severity minor
|
|
1106
|
+
|
|
1107
|
+
Checks that the complementary landmark (role="complementary" or an <aside> that keeps its implicit role) is not nested inside another landmark region.
|
|
1108
|
+
|
|
1109
|
+
**Applies to.** Applies whenever the page contains at least one element carrying the complementary role: explicit role="complementary", or an <aside> that keeps its implicit role (see implementation notes on when it does not).
|
|
1110
|
+
|
|
1111
|
+
**Expectation.** No complementary candidate has an ancestor that is itself a landmark region. Complementary content supports the main content of the page and sits beside it; nested inside another landmark it is a section of that landmark instead, which is not what landmark navigation announces.
|
|
1112
|
+
|
|
1079
1113
|
### `landmark-contentinfo-is-top-level`
|
|
1080
1114
|
|
|
1081
1115
|
**Contentinfo landmark must be top-level**
|
|
@@ -1166,26 +1200,25 @@ Checks that when two or more landmarks share the same role, each has a distinct
|
|
|
1166
1200
|
|
|
1167
1201
|
automatic · WCAG 1.4.1 (A) · confidence high · default severity serious
|
|
1168
1202
|
|
|
1169
|
-
Checks that a link inside a run of text is visually distinguishable from the surrounding text by underline,
|
|
1203
|
+
Checks that a link inside a run of text is visually distinguishable from the surrounding text by a non-color cue (underline, font-weight or style, border, background, icon), and asks about links distinguished only by a >=3:1 color difference, which also need a hover and focus cue.
|
|
1170
1204
|
|
|
1171
|
-
**Applies to.** Applies to
|
|
1205
|
+
**Applies to.** Applies to links (`<a href>` and elements with `role="link"`) whose immediate parent element also has at least one direct-child text node with non-whitespace content (i.e. the link sits inline within a run of plain text, not as a standalone item, e.g. not the sole content of a <li> nav item).
|
|
1172
1206
|
|
|
1173
1207
|
**Expectation.**
|
|
1174
1208
|
|
|
1175
1209
|
A link inside a text block must be visually distinguishable from the surrounding text by at least one non-color means:
|
|
1176
1210
|
|
|
1177
1211
|
- text-decoration: underline, OR
|
|
1178
|
-
- a different font-weight than the surrounding text, OR
|
|
1179
|
-
- a
|
|
1180
|
-
- a contrast ratio of at least 3:1 between the link's text color and the surrounding text's color (WCAG technique G183's threshold, sufficient contrast alone is an accepted alternative to underline).
|
|
1212
|
+
- a different font-weight or font-style than the surrounding text, OR
|
|
1213
|
+
- another visible mark on the link itself: a border, box-shadow or outline, a background color different from the surrounding one, a background image, an image or svg inside it, or ::before/::after content.
|
|
1181
1214
|
|
|
1182
|
-
|
|
1215
|
+
A link with none of these is distinguished by color alone. When its color contrasts with the surrounding text by at least 3:1, technique G183 is met only if hover and focus also bring a non-color cue, which a static scan cannot see, so the link is reported as cantTell. Below 3:1, with contrast confidently computable, color is demonstrably the only cue and the link fails.
|
|
1183
1216
|
|
|
1184
1217
|
### `link-name-present`
|
|
1185
1218
|
|
|
1186
1219
|
**Links have an accessible name**
|
|
1187
1220
|
|
|
1188
|
-
automatic · WCAG 4.1.2 (A) · confidence high · default severity serious
|
|
1221
|
+
automatic · WCAG 2.4.4, 4.1.2 (A) · confidence high · default severity serious
|
|
1189
1222
|
|
|
1190
1223
|
Checks that links expose a non-empty accessible name.
|
|
1191
1224
|
|
|
@@ -1199,7 +1232,7 @@ Checks that links expose a non-empty accessible name.
|
|
|
1199
1232
|
|
|
1200
1233
|
manual · WCAG 2.4.4 (A) · confidence medium · default severity minor
|
|
1201
1234
|
|
|
1202
|
-
Flags links whose full accessible name is a known non-descriptive phrase (e.g. "click here", "read more", "more") or a bare file-format name (e.g. "HTML", "PDF") with no adjacent context naming what it leads to, for manual review of whether the purpose is clear.
|
|
1235
|
+
Flags links whose full accessible name is a known non-descriptive phrase (e.g. "click here", "read more", "more") or a bare file-format name (e.g. "HTML", "PDF") with no adjacent context naming what it leads to, for manual review of whether the purpose is clear. English phrases are always recognized, and German, Spanish, French or Japanese ones when the link is in that language.
|
|
1203
1236
|
|
|
1204
1237
|
**Applies to.** Elements matching `a[href], area[href], [role="link"]` with a non-empty computed accessible name (programmatic first, then "name from content", same two-step resolution as `link-name-present`, same selector too). Links with no name at all are `link-name-present`'s concern, not this rule's.
|
|
1205
1238
|
|
|
@@ -1213,7 +1246,7 @@ automatic · WCAG 1.3.1 (A) · confidence high · default severity serious
|
|
|
1213
1246
|
|
|
1214
1247
|
Checks that <ul>/<ol> elements only have <li>, <script>, or <template> as direct children.
|
|
1215
1248
|
|
|
1216
|
-
**Applies to.** Applies to <ul>/<ol> elements that have at least one direct element child.
|
|
1249
|
+
**Applies to.** Applies to <ul>/<ol> elements that have at least one direct element child and whose role is list: no role attribute, role="list", or a role attribute naming no concrete ARIA role. A <ul>/<ol> given another role (listbox, menubar, tablist, none, ...) is not a list, so its children follow that role's rules instead.
|
|
1217
1250
|
|
|
1218
1251
|
**Expectation.** Every direct element child is <li>, <script>, or <template>. UNLESS it has an explicit `role` attribute, in which case the explicit role wins over the tag entirely: a child is valid iff that role is "listitem" (so `<li role="presentation">`/`<li role="menuitem">` are invalid despite the <li> tag, and conversely a non-<li> element explicitly given `role="listitem"` is valid). A wrapper <div> used for styling (no role at all) still breaks list semantics the same as before.
|
|
1219
1252
|
|
|
@@ -1235,11 +1268,11 @@ Checks that elements with role="listbox" expose a non-empty accessible name.
|
|
|
1235
1268
|
|
|
1236
1269
|
automatic · WCAG 1.3.1 (A) · confidence high · default severity serious
|
|
1237
1270
|
|
|
1238
|
-
Checks that <li> elements are contained by <ul>, <ol>, or an element with role="list".
|
|
1271
|
+
Checks that <li> elements are contained by <ul>, <ol>, <menu>, or an element with role="list".
|
|
1239
1272
|
|
|
1240
1273
|
**Applies to.** Applies to <li> elements that have a parent element.
|
|
1241
1274
|
|
|
1242
|
-
**Expectation.** The parent is <ul>/<ol> with no role override, or an element with an explicit role of "list", "presentation", or "none". An <li> used outside a real list container (e.g. as a generic flex/grid item under a <div>) is not exposed as a list item to assistive technologies.
|
|
1275
|
+
**Expectation.** The parent is <ul>/<ol>/<menu> with no role override (all three have the implicit role list), or an element with an explicit role of "list", "presentation", or "none". An <li> used outside a real list container (e.g. as a generic flex/grid item under a <div>) is not exposed as a list item to assistive technologies.
|
|
1243
1276
|
|
|
1244
1277
|
### `manual-review`
|
|
1245
1278
|
|
|
@@ -1279,11 +1312,11 @@ Checks that menu items (role="menuitem*", including checkbox/radio variants) exp
|
|
|
1279
1312
|
|
|
1280
1313
|
### `meta-refresh-no-exceptions`
|
|
1281
1314
|
|
|
1282
|
-
**Page must not use a meta refresh
|
|
1315
|
+
**Page must not use a timed meta refresh (AAA)**
|
|
1283
1316
|
|
|
1284
1317
|
automatic · WCAG 2.2.4, 3.2.5 (AAA) · confidence high · default severity moderate
|
|
1285
1318
|
|
|
1286
|
-
Checks that <meta http-equiv="refresh">
|
|
1319
|
+
Checks that <meta http-equiv="refresh"> does not set a positive delay, however long; an immediate redirect (delay 0) passes. This is the stricter AAA-level counterpart of the A-level check, which exempts delays over 20 hours.
|
|
1287
1320
|
|
|
1288
1321
|
**Applies to.** Applies to the first <meta http-equiv="refresh"> element, in document order, with a valid content attribute, per HTML's shared declarative refresh steps, a document only ever acts on its first valid meta refresh, so a later one (valid or not) is inert and out of scope.
|
|
1289
1322
|
|
|
@@ -1347,7 +1380,7 @@ Flags elements with an inline pointer-only event handler (onmouseover, onmouseou
|
|
|
1347
1380
|
|
|
1348
1381
|
**Applies to.** Elements carrying at least one inline pointer-only event-handler attribute (`onmouseover`, `onmouseout`, `onmousedown`, `onmouseup`, `ondblclick`, `onmousemove`, `onmouseenter`, `onmouseleave`) with a non-empty value, that are also eligible/reachable (not hidden/`aria-hidden`/`display:none`).
|
|
1349
1382
|
|
|
1350
|
-
**Expectation.** The element also carries at least one keyboard-reachable inline handler: `onkeydown`, `onkeyup`, `onkeypress` (the direct keyboard- event equivalents), or `onfocus`/`onblur` (the standard substitute for hover-triggered behavior: focus/blur are the keyboard- navigable analog to mouseover/mouseout, per WCAG technique G90). Otherwise the element's mouse-driven behavior (a hover tooltip, a custom dropdown, a drag interaction) has no way to be triggered by a keyboard-only user.
|
|
1383
|
+
**Expectation.** The element also carries at least one keyboard-reachable inline handler: `onkeydown`, `onkeyup`, `onkeypress` (the direct keyboard- event equivalents), or `onfocus`/`onblur` (the standard substitute for hover-triggered behavior: focus/blur are the keyboard- navigable analog to mouseover/mouseout, per WCAG technique G90). A handler is reachable only if keyboard events can reach it: `onfocus`/`onblur` when the element itself can take focus, and a key handler when the element or one of its descendants can (key events bubble, focus events do not). Otherwise the element's mouse-driven behavior (a hover tooltip, a custom dropdown, a drag interaction) has no way to be triggered by a keyboard-only user, and it is flagged with a reason saying whether the keyboard handlers are missing or cannot run.
|
|
1351
1384
|
|
|
1352
1385
|
### `nested-interactive-controls-absent`
|
|
1353
1386
|
|
|
@@ -1367,9 +1400,9 @@ Checks that an interactive control (link, button, form control, or ARIA widget r
|
|
|
1367
1400
|
|
|
1368
1401
|
manual · WCAG 1.4.2 (A) · confidence low · default severity moderate
|
|
1369
1402
|
|
|
1370
|
-
Flags <audio>/<video> elements that autoplay unmuted with no native controls attribute, for manual review against the 3-second exemption in WCAG 1.4.2.
|
|
1403
|
+
Flags <audio>/<video> elements that autoplay unmuted with no native controls attribute, and <embed>, <object> or <bgsound> elements that may play sound, for manual review against the 3-second exemption in WCAG 1.4.2.
|
|
1371
1404
|
|
|
1372
|
-
**Applies to.** Any <audio autoplay> or <video autoplay> element that is not `muted`.
|
|
1405
|
+
**Applies to.** Any <audio autoplay> or <video autoplay> element that is not `muted`. Also any <bgsound>, and any <embed> or <object> that loads sound or video, or a plugin (Flash) that may play it: its `type` is audio/*, video/* or a plugin type, or its `src`/`data` ends in a sound or video file extension. An <embed> or <object> with `autostart` or `autoplay` set to false (attribute or <param>) is left out.
|
|
1373
1406
|
|
|
1374
1407
|
**Expectation.** SC 1.4.2 only applies when audio plays automatically for MORE than 3 seconds; clip duration is not knowable from static markup (jsdom does not decode media), so this rule cannot determine whether the SC even applies to a given element. It is authored as `type: 'manual'` (cantTell-capped, never fail) on purpose rather than guessing: an autoplaying unmuted element with no `controls` attribute (the native, statically-verifiable mechanism to pause/stop or adjust volume) is flagged for human review rather than treated as a deterministic violation.
|
|
1375
1408
|
|
|
@@ -1417,13 +1450,13 @@ Checks that elements with role="option" expose a non-empty accessible name.
|
|
|
1417
1450
|
|
|
1418
1451
|
### `p-as-heading`
|
|
1419
1452
|
|
|
1420
|
-
**
|
|
1453
|
+
**Text styled to look like a heading should probably be a real heading**
|
|
1421
1454
|
|
|
1422
1455
|
manual · WCAG 1.3.1 (A) · confidence low · default severity minor
|
|
1423
1456
|
|
|
1424
|
-
Flags short <p> elements whose
|
|
1457
|
+
Flags short <p> and <div> elements whose text is all bold and rendered at >=18px, for manual review of whether a real heading element should be used instead.
|
|
1425
1458
|
|
|
1426
|
-
**Applies to.** `<p>` elements with short (<=120 char), non-empty trimmed text
|
|
1459
|
+
**Applies to.** `<p>` elements, and `<div>` elements that hold only text and inline markup, with short (<=120 char), non-empty trimmed text in which every piece of text is bold (computed `font-weight` >= 700, however it got there: on the element itself, a `<strong>`/`<b>` or a styled `<span>`) and rendered at >=18px.
|
|
1427
1460
|
|
|
1428
1461
|
**Expectation.** Text styled to visually read as a heading (bold, larger-than-body size, short) should be marked up with a real heading element (`<h1>`-`<h6>` or `role="heading"`) so its structural role is programmatically determinable, the same 1.3.1 concern as any other "structure conveyed through presentation only" issue.
|
|
1429
1462
|
|
|
@@ -1447,7 +1480,7 @@ manual · WCAG 2.4.2 (A) · confidence medium · default severity minor
|
|
|
1447
1480
|
|
|
1448
1481
|
Identifies page title patterns that may indicate low descriptiveness, such as generic, duplicated, or overly templated titles. This rule provides review signals and does not fail automatically.
|
|
1449
1482
|
|
|
1450
|
-
**Applies to.** Applies to a run over a whole document whose <title> resolves to non-empty text; a missing or empty title is page-title-present's failure, not a pattern to review. A run narrowed by contextSelector or by engineOptions.fragment is notApplicable, as is a title matching none of the patterns below.
|
|
1483
|
+
**Applies to.** Applies to a run over a whole document whose <title> resolves to non-empty text. The title element is the first HTML <title> anywhere in the document, as for document.title, so one the parser left in <body> counts; a missing or empty title is page-title-present's failure, not a pattern to review. A run narrowed by contextSelector or by engineOptions.fragment is notApplicable, as is a title matching none of the patterns below.
|
|
1451
1484
|
|
|
1452
1485
|
**Expectation.** The title carries none of the conservative low-descriptiveness signals: one of the generic titles home, homepage, welcome, untitled, page or document; fewer than eight characters; or a template shape pairing a generic token with a brand, such as "Home | Brand". When the crawl.pageTitles probe supplies at least ten pages, cross-page signals are used instead: one title repeated across distinct URLs, or a prefix or suffix of twelve characters or more shared across the set. Every signal is reported as cantTell: whether a title describes its page is a judgment, so the rule never fails on a pattern alone.
|
|
1453
1486
|
|
|
@@ -1461,7 +1494,19 @@ Checks that the page includes a non-empty <title> element.
|
|
|
1461
1494
|
|
|
1462
1495
|
**Applies to.** Applies to a run over a whole document. A run narrowed by contextSelector, or by engineOptions.fragment, is notApplicable: whether the page has a title is not a property any subtree can answer.
|
|
1463
1496
|
|
|
1464
|
-
**Expectation.** The document has a <title> element, and document.title with whitespace collapsed is non-empty. The element is looked for anywhere in the document, not only inside <head>: a <title> the parser leaves outside <head> is still the document title in every browser. Whether that title describes the page is page-title-patterns' question.
|
|
1497
|
+
**Expectation.** The document has a <title> element, and document.title with whitespace collapsed is non-empty. The element is looked for anywhere in the document, not only inside <head>: a <title> the parser leaves outside <head> is still the document title in every browser. Only an HTML-namespace <title> counts; the <title> of an inline <svg> names the graphic, so a page whose only <title> is inside an <svg> is missing its title element. Whether that title describes the page is page-title-patterns' question.
|
|
1498
|
+
|
|
1499
|
+
### `password-paste-enabled`
|
|
1500
|
+
|
|
1501
|
+
**Authentication fields must not block pasting**
|
|
1502
|
+
|
|
1503
|
+
manual · WCAG 3.3.8 (AA) · confidence medium · default severity serious
|
|
1504
|
+
|
|
1505
|
+
Checks that a password or one-time-code field carries no inline paste handler that cancels the paste, which would remove the password manager or clipboard that WCAG 3.3.8 relies on as the assisting mechanism.
|
|
1506
|
+
|
|
1507
|
+
**Applies to.** Applies to any control whose autocomplete token is current-password, new-password or one-time-code, and to <input type="password"> unless its autocomplete names another purpose. A disabled or readonly field takes no input to block, and one outside the accessibility tree is not being asked for, so neither is in scope.
|
|
1508
|
+
|
|
1509
|
+
**Expectation.** A reviewer confirms the field can still be pasted into. Remembering a password is a cognitive function test, and 3.3.8 asks for a mechanism that helps the user through one; a password manager, or the clipboard for a one-time code, is that mechanism.
|
|
1465
1510
|
|
|
1466
1511
|
### `presentation-role-conflict`
|
|
1467
1512
|
|
|
@@ -1497,7 +1542,7 @@ Checks that elements with role="progressbar" expose a non-empty accessible name.
|
|
|
1497
1542
|
|
|
1498
1543
|
**Applies to.** Applies to elements carrying role="progressbar" (the attribute must name that role alone, not a fallback list) that are included in the accessibility tree. An element with the matching implicit role but no role attribute is out of scope.
|
|
1499
1544
|
|
|
1500
|
-
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="progressbar" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom progressbar widget is not reliably exposed as its name.
|
|
1545
|
+
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="progressbar" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom progressbar widget is not reliably exposed as its name. On a labelable element (<progress role="progressbar">) an associated <label> counts as well.
|
|
1501
1546
|
|
|
1502
1547
|
### `region`
|
|
1503
1548
|
|
|
@@ -1519,7 +1564,7 @@ automatic · WCAG 1.1.1 (A) · confidence high · default severity serious
|
|
|
1519
1564
|
|
|
1520
1565
|
Checks that elements with role="img", "graphics-symbol" or "graphics-document" provide an accessible text alternative using aria-label, aria-labelledby, a title attribute, or (for SVG elements) a first-child <title>.
|
|
1521
1566
|
|
|
1522
|
-
**Applies to.** Applies to elements with role="img", role="graphics-symbol" or role="graphics-document" that are included in the accessibility tree (ACT 23a2a8's "programmatically hidden" exemption: display:none/visibility:hidden/aria-hidden="true" on the element or an ancestor, with no carve-out for focusable or IDREF-referenced elements, aria-hidden-focus and duplicate-id-aria own those separately). Per ACT 7d6734, this reaches any element carrying one of these roles, not only the <svg> root, e.g. a <circle role="graphics-symbol"> nested inside a plain <svg>.
|
|
1567
|
+
**Applies to.** Applies to elements with role="img", role="graphics-symbol" or role="graphics-document" that are included in the accessibility tree (ACT 23a2a8's "programmatically hidden" exemption: display:none/visibility:hidden/aria-hidden="true" on the element or an ancestor, with no carve-out for focusable or IDREF-referenced elements, aria-hidden-focus and duplicate-id-aria own those separately). Per ACT 7d6734, this reaches any element carrying one of these roles, not only the <svg> root, e.g. a <circle role="graphics-symbol"> nested inside a plain <svg>. Every <svg> element itself is left to svg-text-alternative-present's own (already ACT-clean) title/aria-name handling, so an unnamed <svg role="img"> is reported once, there; the <img> tag is excluded here since it has its own dedicated rule.
|
|
1523
1568
|
|
|
1524
1569
|
**Expectation.**
|
|
1525
1570
|
|
|
@@ -1564,19 +1609,19 @@ Checks that elements with role="searchbox" expose a non-empty accessible name.
|
|
|
1564
1609
|
|
|
1565
1610
|
**Applies to.** Applies to elements carrying role="searchbox" (the attribute must name that role alone, not a fallback list) that are included in the accessibility tree. An element with the matching implicit role but no role attribute is out of scope.
|
|
1566
1611
|
|
|
1567
|
-
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="searchbox" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom searchbox widget is not reliably exposed as its name. On a labelable element (<input role="searchbox">) an associated <label> counts as well.
|
|
1612
|
+
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="searchbox" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom searchbox widget is not reliably exposed as its name. On a labelable element (<input role="searchbox">) an associated <label> counts as well. On a text-like <input> or a <textarea>, the placeholder counts last (HTML-AAM's final name source): a placeholder-only label is a 3.3.2 question, not a missing name.
|
|
1568
1613
|
|
|
1569
1614
|
### `server-side-image-map-absent`
|
|
1570
1615
|
|
|
1571
|
-
**
|
|
1616
|
+
**Server-side image maps must have a keyboard-operable alternative**
|
|
1572
1617
|
|
|
1573
1618
|
automatic · WCAG 2.1.1 (A) · confidence high · default severity serious
|
|
1574
1619
|
|
|
1575
|
-
|
|
1620
|
+
Asks, for each <img ismap> inside a link, whether the page offers the same destinations as links a keyboard can reach, since a server-side image map has no keyboard-operable regions of its own.
|
|
1576
1621
|
|
|
1577
|
-
**Applies to.** Applies to <img>
|
|
1622
|
+
**Applies to.** Applies to any scan scope. An <img ismap> inside an <a href> is a server-side image map: the browser sends the click coordinates to the link's URL, which has no keyboard-operable equivalent and exposes no individual regions to assistive technology.
|
|
1578
1623
|
|
|
1579
|
-
**Expectation.**
|
|
1624
|
+
**Expectation.** Each server-side image map is reported as cantTell: 2.1.1 is met when the same destinations are also offered as links a keyboard can reach, which the rule cannot verify. Client-side image maps (<map>/<area>) are not flagged. A scope with no <img ismap> passes. One whose only <img ismap> elements are outside a link is notApplicable: ismap does nothing there, so there is no server-side image map (the misplaced attribute is invalid HTML, a matter for the validator).
|
|
1580
1625
|
|
|
1581
1626
|
### `skip-link`
|
|
1582
1627
|
|
|
@@ -1586,7 +1631,14 @@ manual · no formal WCAG SC mapping · confidence medium · default severity min
|
|
|
1586
1631
|
|
|
1587
1632
|
Checks that a "skip to ..." link's href fragment resolves to a real, currently usable element in the document.
|
|
1588
1633
|
|
|
1589
|
-
**Applies to.**
|
|
1634
|
+
**Applies to.**
|
|
1635
|
+
|
|
1636
|
+
Applies to <a href="#fragment"> elements that are skip links by one of two signs:
|
|
1637
|
+
|
|
1638
|
+
- the accessible name follows a common skip-link wording in one of the shipped locales: "skip" or "jump to" (English); "aller au contenu", "passer au contenu", "accéder au contenu", "accès direct", "évitement" (French); "springen", "überspringen", "direkt zum", "zum Inhalt" (German); "saltar", "ir al contenido" (Spanish); "スキップ", "本文へ" (Japanese). "jump to" sits beside "skip" because real skip links use both conventions (e.g. a "Jump to section" link);
|
|
1639
|
+
- or it is the first link in the document, and it comes before the `main` element (or `[role="main"]`): the usual place of a skip link whatever its wording.
|
|
1640
|
+
|
|
1641
|
+
Other same-page anchor links are not skip links and are left alone.
|
|
1590
1642
|
|
|
1591
1643
|
**Expectation.** The link's fragment resolves to a real element in the document (via a matching id, or a legacy <a name="...">), and that target is currently usable (not hidden from the accessibility tree; and, when browser geometry is available, not zero-area/no-rects). A skip link whose target is missing or effectively unusable does not provide a reliable bypass destination.
|
|
1592
1644
|
|
|
@@ -1600,7 +1652,7 @@ Checks that sliders (input[type="range"] and role="slider") expose a non-empty a
|
|
|
1600
1652
|
|
|
1601
1653
|
**Applies to.** Applies to elements carrying role="slider" (the attribute must name that role alone, not a fallback list) that are included in the accessibility tree. An element with the matching implicit role but no role attribute is out of scope. A native <input type="range"> without the role belongs to form-control-programmatic-label-present.
|
|
1602
1654
|
|
|
1603
|
-
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="slider" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom slider widget is not reliably exposed as its name. On a labelable element (<input role="slider">) an associated <label> counts as well.
|
|
1655
|
+
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="slider" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom slider widget is not reliably exposed as its name. On a labelable element (<input role="slider">) an associated <label> counts as well. On a text-like <input> or a <textarea>, the placeholder counts last (HTML-AAM's final name source): a placeholder-only label is a 3.3.2 question, not a missing name.
|
|
1604
1656
|
|
|
1605
1657
|
### `spinbutton-name-present`
|
|
1606
1658
|
|
|
@@ -1612,7 +1664,7 @@ Checks that elements with role="spinbutton" expose a non-empty accessible name.
|
|
|
1612
1664
|
|
|
1613
1665
|
**Applies to.** Applies to elements carrying role="spinbutton" (the attribute must name that role alone, not a fallback list) that are included in the accessibility tree. An element with the matching implicit role but no role attribute is out of scope.
|
|
1614
1666
|
|
|
1615
|
-
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="spinbutton" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom spinbutton widget is not reliably exposed as its name. On a labelable element (<input role="spinbutton">) an associated <label> counts as well.
|
|
1667
|
+
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="spinbutton" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom spinbutton widget is not reliably exposed as its name. On a labelable element (<input role="spinbutton">) an associated <label> counts as well. On a text-like <input> or a <textarea>, the placeholder counts last (HTML-AAM's final name source): a placeholder-only label is a 3.3.2 question, not a missing name.
|
|
1616
1668
|
|
|
1617
1669
|
### `summary-name-present`
|
|
1618
1670
|
|
|
@@ -1728,7 +1780,7 @@ manual · WCAG 1.3.1 (A) · confidence low · default severity minor
|
|
|
1728
1780
|
|
|
1729
1781
|
Flags tables with no <caption> whose first row has a single non-empty cell while other rows have multiple cells, for manual review of whether that cell is acting as a fake caption.
|
|
1730
1782
|
|
|
1731
|
-
**Applies to.** `<table>` elements with no `<caption>` child, at least two rows, and a first row containing exactly one non-empty-text cell while at least one other row has more than one cell.
|
|
1783
|
+
**Applies to.** `<table>` elements with no `<caption>` child, at least two rows, and a first row containing exactly one non-empty-text cell while at least one other row has more than one cell. Left out: a table whose role (first token) is anything but table, grid or treegrid, such as a layout table with role="presentation", which needs no caption; and a table already named by a non-empty aria-label, an aria-labelledby that resolves to text, or a non-empty title, which WCAG accepts as the table's title.
|
|
1732
1784
|
|
|
1733
1785
|
**Expectation.** A single lone cell in the first row, sitting above rows that clearly have multiple columns, strongly suggests the author is using it as a visual caption/title rather than as a real table cell. Structure conveyed only through this positional convention is not programmatically associated with the table the way a real `<caption>` element is (1.3.1).
|
|
1734
1786
|
|
|
@@ -1794,15 +1846,36 @@ automatic · WCAG 1.3.1 (A) · confidence high · default severity serious
|
|
|
1794
1846
|
|
|
1795
1847
|
Checks that every <td> in a large, simple (no colspan/rowspan) table has an associated header, via a headers attribute, an implicit column <th> above it, or an implicit row <th> to its left.
|
|
1796
1848
|
|
|
1797
|
-
**Applies to.** `<table>` elements with at least 4 rows and at least 4 columns (a "large" table, where implicit row/column header association is useful; small tables are usually self-evident), and with NO `colspan`/`rowspan` anywhere in the table.
|
|
1849
|
+
**Applies to.** `<table>` elements with at least 4 rows and at least 4 columns (a "large" table, where implicit row/column header association is useful; small tables are usually self-evident), and with NO `colspan`/`rowspan` anywhere in the table. A table whose role (first token) is anything but table, grid or treegrid, such as a layout table with role="presentation", is left out: it has no data cells.
|
|
1798
1850
|
|
|
1799
1851
|
**Expectation.**
|
|
1800
1852
|
|
|
1801
|
-
Every `<td>` has an associated header, via one of:
|
|
1853
|
+
Every non-empty `<td>` has an associated header, via one of:
|
|
1802
1854
|
|
|
1803
1855
|
- a non-empty `headers` attribute (trusted here; whether it resolves to real `<th>` ids is `table-headers-attr-valid`'s concern, not this rule's), OR
|
|
1804
|
-
- an implicit column header:
|
|
1805
|
-
- an implicit row header:
|
|
1856
|
+
- an implicit column header: a header cell in the same column, in an earlier row, OR
|
|
1857
|
+
- an implicit row header: a header cell earlier in the same row.
|
|
1858
|
+
|
|
1859
|
+
A header cell is a `<th>` with no other role, or any cell with role="columnheader" or role="rowheader" (such a `<td>` is a header, not a data cell). A `<td>` with no text and no content that could carry a name (an image, a control, an element with an ARIA label) holds no data, so it needs no header; the empty corner cell above row headers is the usual case.
|
|
1860
|
+
|
|
1861
|
+
### `text-spacing-content-loss`
|
|
1862
|
+
|
|
1863
|
+
**Text stays readable when the user increases text spacing**
|
|
1864
|
+
|
|
1865
|
+
automatic · WCAG 1.4.12 (AA) · confidence medium · default severity serious
|
|
1866
|
+
|
|
1867
|
+
Applies the WCAG 1.4.12 text spacing in the browser and checks that no text is cut off or made to overlap, and asks about style sheet rules that force spacing with !important.
|
|
1868
|
+
|
|
1869
|
+
**Applies to.** Applies to a page with visible text. The loss of content needs a layout (a browser): without one, only the style sheets are read.
|
|
1870
|
+
|
|
1871
|
+
**Expectation.**
|
|
1872
|
+
|
|
1873
|
+
WCAG 1.4.12: with line height at 1.5 times the font size, spacing after paragraphs at 2 times, letter spacing at 0.12 times and word spacing at 0.16 times, no content or functionality is lost.
|
|
1874
|
+
|
|
1875
|
+
- In a browser, the spacing is applied as a style sheet that wins over the page's own (inline `!important` aside, which avoid-inline-spacing reports), and each line of text is measured before and after against the ancestors that clip it (`overflow: hidden` or `clip`). A line that was inside and ends at least half outside (half its height, or half an em across) fails: the container cuts that text off (TEXT_CLIPPED). A line pushed out by less is asked about (TEXT_CLIPPED_PARTLY), and so is text that comes to overlap other text it did not overlap before (TEXT_OVERLAPS).
|
|
1876
|
+
- In any environment, a style sheet rule that sets line-height, letter-spacing or word-spacing below those values with `!important` is asked about (STYLESHEET_IMPORTANT): a tool that adds its own style sheet to the page cannot override it, though a user style sheet can.
|
|
1877
|
+
|
|
1878
|
+
Without a layout and with no such rule, the rule is notApplicable, with `data.reason: 'noLayout'`.
|
|
1806
1879
|
|
|
1807
1880
|
### `textbox-name-present`
|
|
1808
1881
|
|
|
@@ -1814,7 +1887,7 @@ Checks that elements with role="textbox" expose a non-empty accessible name.
|
|
|
1814
1887
|
|
|
1815
1888
|
**Applies to.** Applies to elements carrying role="textbox" (the attribute must name that role alone, not a fallback list) that are included in the accessibility tree. An element with the matching implicit role but no role attribute is out of scope.
|
|
1816
1889
|
|
|
1817
|
-
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="textbox" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom textbox widget is not reliably exposed as its name. On a labelable element (<input role="textbox">) an associated <label> counts as well.
|
|
1890
|
+
**Expectation.** The element has a non-empty accessible name from aria-label, from an aria-labelledby that resolves to non-empty text, or from title. role="textbox" is name-from-author-only, so subtree text is never accepted: text sitting inside a custom textbox widget is not reliably exposed as its name. On a labelable element (<input role="textbox">) an associated <label> counts as well. On a text-like <input> or a <textarea>, the placeholder counts last (HTML-AAM's final name source): a placeholder-only label is a 3.3.2 question, not a missing name.
|
|
1818
1891
|
|
|
1819
1892
|
### `tooltip-name-present`
|
|
1820
1893
|
|
|
@@ -1856,7 +1929,7 @@ Applies to any element other than the root <html> with a non-empty lang at
|
|
|
1856
1929
|
- A non-empty alt attribute on img/area/input[type=image] counts as governed text, the same as a text node.
|
|
1857
1930
|
- Text (or alt) that CSS keeps out of the render tree (display:none, the hidden attribute, ...) does not count. aria-hidden and offscreen positioning do NOT exempt text, per ACT's own failed examples for both, only actual non-rendering does.
|
|
1858
1931
|
|
|
1859
|
-
**Expectation.** The lang value
|
|
1932
|
+
**Expectation.** The primary language subtag of the lang value (the part before the first hyphen) is a registered language subtag, as ACT de46e4 requires. WCAG 3.1.2 (Language of Parts) requires that when a passage's language differs from the page's default, it is identified programmatically. A tag whose primary subtag is unknown fails to identify a real language at all; a malformed later subtag (lang="en-US_x") still identifies English and passes here, since it is a markup validity error rather than a missing language.
|
|
1860
1933
|
|
|
1861
1934
|
### `video-caption`
|
|
1862
1935
|
|
|
@@ -1864,11 +1937,11 @@ Applies to any element other than the root <html> with a non-empty lang at
|
|
|
1864
1937
|
|
|
1865
1938
|
manual · WCAG 1.2.2 (A) · confidence low · default severity moderate
|
|
1866
1939
|
|
|
1867
|
-
Flags <video> elements with no <track kind="captions"
|
|
1940
|
+
Flags <video> elements with no <track kind="captions"> child, for manual review of whether the video has an audio track that needs captions; a subtitles track alone may be a translation only.
|
|
1868
1941
|
|
|
1869
1942
|
**Applies to.** Any <video> element in the composed DOM.
|
|
1870
1943
|
|
|
1871
|
-
**Expectation.** SC 1.2.2 requires captions for prerecorded synchronized media, but only when the video actually has an audio track that conveys information (a silent/decorative video needs none), which cannot be verified from static markup alone (jsdom does not decode media). This rule is therefore `type: 'manual'` (cantTell-capped, never fail), matching the precedent set by `media-alternative-transcript-evidence` for the same class of "normatively mapped but not statically verifiable" gap. A <video> with a `<track kind="captions">`
|
|
1944
|
+
**Expectation.** SC 1.2.2 requires captions for prerecorded synchronized media, but only when the video actually has an audio track that conveys information (a silent/decorative video needs none), which cannot be verified from static markup alone (jsdom does not decode media). This rule is therefore `type: 'manual'` (cantTell-capped, never fail), matching the precedent set by `media-alternative-transcript-evidence` for the same class of "normatively mapped but not statically verifiable" gap. A <video> with a `<track kind="captions">` whose `src` is non-empty is not flagged; everything else is flagged for human review. A video whose only text tracks are subtitles (`kind="subtitles"`, or no `kind`, which HTML treats as subtitles) gets its own question: subtitles may be a translation of the dialogue only, without the speaker and sound information captions carry.
|
|
1872
1945
|
|
|
1873
1946
|
### `video-poster-text-alternative-present`
|
|
1874
1947
|
|