@uuv/a11y 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +199 -0
- package/bundle/uuv-a11y.bundle.js +3 -0
- package/bundle/uuv-a11y.bundle.js.LICENSE.txt +28 -0
- package/dist/README.md +199 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -0
- package/dist/lib/engine/engine.d.ts +12 -0
- package/dist/lib/engine/engine.js +123 -0
- package/dist/lib/index.d.ts +3 -0
- package/dist/lib/index.js +19 -0
- package/dist/lib/model/checker.d.ts +14 -0
- package/dist/lib/model/checker.js +35 -0
- package/dist/lib/model/index.d.ts +4 -0
- package/dist/lib/model/index.js +20 -0
- package/dist/lib/model/reference.d.ts +55 -0
- package/dist/lib/model/reference.js +66 -0
- package/dist/lib/model/result.d.ts +57 -0
- package/dist/lib/model/result.js +144 -0
- package/dist/lib/model/rule.d.ts +99 -0
- package/dist/lib/model/rule.js +53 -0
- package/dist/lib/query/00-query.d.ts +4 -0
- package/dist/lib/query/00-query.js +2 -0
- package/dist/lib/query/accessible-name.query.d.ts +8 -0
- package/dist/lib/query/accessible-name.query.js +34 -0
- package/dist/lib/query/by-role.query.d.ts +8 -0
- package/dist/lib/query/by-role.query.js +27 -0
- package/dist/lib/query/by-tag.query.d.ts +7 -0
- package/dist/lib/query/by-tag.query.js +20 -0
- package/dist/lib/query/doctype.query.d.ts +5 -0
- package/dist/lib/query/doctype.query.js +15 -0
- package/dist/lib/query/form.query.d.ts +8 -0
- package/dist/lib/query/form.query.js +87 -0
- package/dist/lib/query/index.d.ts +6 -0
- package/dist/lib/query/index.js +22 -0
- package/dist/lib/reference/alix/alix-checker.d.ts +0 -0
- package/dist/lib/reference/alix/alix-checker.js +82 -0
- package/dist/lib/reference/alix/alix-rules.d.ts +0 -0
- package/dist/lib/reference/alix/alix-rules.js +1028 -0
- package/dist/lib/reference/alix/index.d.ts +0 -0
- package/dist/lib/reference/alix/index.js +3 -0
- package/dist/lib/reference/index.d.ts +1 -0
- package/dist/lib/reference/index.js +18 -0
- package/dist/lib/reference/rgaa/common.d.ts +2 -0
- package/dist/lib/reference/rgaa/common.js +5 -0
- package/dist/lib/reference/rgaa/coverage/coverage-helper.d.ts +2 -0
- package/dist/lib/reference/rgaa/coverage/coverage-helper.js +100 -0
- package/dist/lib/reference/rgaa/coverage/coverage-statement.json +90 -0
- package/dist/lib/reference/rgaa/index.d.ts +2 -0
- package/dist/lib/reference/rgaa/index.js +18 -0
- package/dist/lib/reference/rgaa/rgaa-checker.d.ts +8 -0
- package/dist/lib/reference/rgaa/rgaa-checker.js +31 -0
- package/dist/lib/reference/rgaa/rgaa-reference.d.ts +2 -0
- package/dist/lib/reference/rgaa/rgaa-reference.js +44 -0
- package/dist/lib/reference/rgaa/rgaa_4.1.criteres.json +4144 -0
- package/dist/lib/reference/rgaa/rules/1-image.d.ts +14 -0
- package/dist/lib/reference/rgaa/rules/1-image.js +644 -0
- package/dist/lib/reference/rgaa/rules/10-display.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/10-display.js +3 -0
- package/dist/lib/reference/rgaa/rules/11-form.d.ts +14 -0
- package/dist/lib/reference/rgaa/rules/11-form.js +16 -0
- package/dist/lib/reference/rgaa/rules/12-navigation.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/12-navigation.js +3 -0
- package/dist/lib/reference/rgaa/rules/13-visit.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/13-visit.js +3 -0
- package/dist/lib/reference/rgaa/rules/2-frame.d.ts +14 -0
- package/dist/lib/reference/rgaa/rules/2-frame.js +34 -0
- package/dist/lib/reference/rgaa/rules/3-color.d.ts +14 -0
- package/dist/lib/reference/rgaa/rules/3-color.js +50 -0
- package/dist/lib/reference/rgaa/rules/4-multimedia.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/4-multimedia.js +3 -0
- package/dist/lib/reference/rgaa/rules/5-table.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/5-table.js +3 -0
- package/dist/lib/reference/rgaa/rules/6-link.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/6-link.js +3 -0
- package/dist/lib/reference/rgaa/rules/7-script.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/7-script.js +3 -0
- package/dist/lib/reference/rgaa/rules/8-required-element.d.ts +14 -0
- package/dist/lib/reference/rgaa/rules/8-required-element.js +78 -0
- package/dist/lib/reference/rgaa/rules/9-structure.d.ts +2 -0
- package/dist/lib/reference/rgaa/rules/9-structure.js +3 -0
- package/dist/lib/reference/rgaa/selector-helper.d.ts +17 -0
- package/dist/lib/reference/rgaa/selector-helper.js +33 -0
- package/dist/package.json +63 -0
- package/package.json +63 -0
|
@@ -0,0 +1,1028 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// import { A11yReference } from "../../model/reference";
|
|
3
|
+
// import { AutoCheckA11yRule } from "../../model/rule";
|
|
4
|
+
// import { ByTagQuery } from "../../query";
|
|
5
|
+
//
|
|
6
|
+
// export const A11Y_ALIX_RULES : A11yReference = {
|
|
7
|
+
// name: "ALIX",
|
|
8
|
+
// version: "4.1",
|
|
9
|
+
// rules: [
|
|
10
|
+
// AutoCheckA11yRule.from({
|
|
11
|
+
// reference: "a11y.css",
|
|
12
|
+
// criterion: "Attribute should not contain whitespace",
|
|
13
|
+
// wcag: "",
|
|
14
|
+
// id: "",
|
|
15
|
+
// elementType: "error - attribute-whitespace",
|
|
16
|
+
// query: new ByTagQuery([
|
|
17
|
+
// "[id*=\" \"], [lang*=\" \"], map[name*=\" \"]"
|
|
18
|
+
// ]),
|
|
19
|
+
// description: "Some HTML attributes should not contain any whitespace —namely [id], [lang] and map[name].",
|
|
20
|
+
// help: "https://html.spec.whatwg.org/#the-id-attribute\nhttps://html.spec.whatwg.org/#the-map-element"
|
|
21
|
+
// }),
|
|
22
|
+
// AutoCheckA11yRule.from({
|
|
23
|
+
// reference: "a11y.css",
|
|
24
|
+
// criterion: "[tabindex] > 0",
|
|
25
|
+
// wcag: "",
|
|
26
|
+
// id: "",
|
|
27
|
+
// elementType: "error - tab-order",
|
|
28
|
+
// query: new ByTagQuery(["[tabindex]:not([tabindex=\"0\"], [tabindex^=\"-\"])"]),
|
|
29
|
+
// description: "The [tabindex] attribute should never be greater than 0.",
|
|
30
|
+
// help: "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L337\nhttps://www.w3.org/WAI/WCAG21/Techniques/failures/F44\nhttps://www.scottohara.me/blog/2019/05/25/tabindex.html"
|
|
31
|
+
// }),
|
|
32
|
+
// AutoCheckA11yRule.from({
|
|
33
|
+
// reference: "a11y.css",
|
|
34
|
+
// criterion: "Empty [href]",
|
|
35
|
+
// wcag: "",
|
|
36
|
+
// id: "",
|
|
37
|
+
// elementType: "error - empty-href",
|
|
38
|
+
// query: new ByTagQuery(["a[href=\"\"], a[href=\" \"]"]),
|
|
39
|
+
// description: "The [href] attribute, if present, should not be empty. A link to something, right?",
|
|
40
|
+
// help: "https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements\nhttps://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L161\nhttps://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L165"
|
|
41
|
+
// }),
|
|
42
|
+
// AutoCheckA11yRule.from({
|
|
43
|
+
// reference: "a11y.css",
|
|
44
|
+
// criterion: "Empty link",
|
|
45
|
+
// wcag: "",
|
|
46
|
+
// id: "",
|
|
47
|
+
// elementType: "error - empty-link",
|
|
48
|
+
// query: new ByTagQuery(["a:empty[title=\"\"], a:empty[aria-label=\"\"], a:empty[aria-labelledby=\"\"], a:empty:not([title], [aria-label], [aria-labelledby])"]),
|
|
49
|
+
// description: "An empty link should have a label, within [title], [aria-label] or targeted by [aria-labelledby]. By the way, why would you use an empty link?",
|
|
50
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#6.2\nhttps://www.w3.org/TR/WCAG21/#non-text-content\nhttps://www.w3.org/TR/WCAG21/#link-purpose-in-context\nhttps://www.w3.org/TR/WCAG21/#link-purpose-link-only\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H30\nhttps://www.w3.org/WAI/WCAG21/Techniques/general/G91\nhttps://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L193"
|
|
51
|
+
// }),
|
|
52
|
+
// AutoCheckA11yRule.from({
|
|
53
|
+
// reference: "a11y.css",
|
|
54
|
+
// criterion: "Missing alternative for img",
|
|
55
|
+
// wcag: "",
|
|
56
|
+
// id: "",
|
|
57
|
+
// elementType: "error - no-alt",
|
|
58
|
+
// query: new ByTagQuery(["img[alt=\" \"], area[alt=\" \"], input[type=\"image\"][alt=\" \"], img:not([alt]), area:not([alt]), input[type=\"image\"]:not([alt])"]),
|
|
59
|
+
// description: "An <img> must have an [alt]. Always.",
|
|
60
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#1.1\nhttps://www.w3.org/WAI/tutorials/images/decision-tree/\nhttps://html.spec.whatwg.org/multipage/images.html#alt\nhttps://www.w3.org/TR/WCAG21/#non-text-content\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H36\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H37\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H24\nhttps://www.w3.org/WAI/WCAG21/Techniques/failures/F65"
|
|
61
|
+
// }),
|
|
62
|
+
// AutoCheckA11yRule.from({
|
|
63
|
+
// reference: "a11y.css",
|
|
64
|
+
// criterion: "Missing label for [role=img]",
|
|
65
|
+
// wcag: "",
|
|
66
|
+
// id: "",
|
|
67
|
+
// elementType: "error - no-aria-label",
|
|
68
|
+
// query: new ByTagQuery(["[role=\"img\"]:not([aria-hidden=\"true\"], [aria-label], [aria-labelledby]), svg[role=\"img\"]:not([aria-hidden=\"true\"], [aria-label], [aria-labelledby])"]),
|
|
69
|
+
// description: "[role=img] without [aria-hidden=true] should either have [aria-label] or [aria-labelledby]. If image is decorative, please use [role=presentation] instead.",
|
|
70
|
+
// help: "https://www.w3.org/TR/wai-aria-1.2/#img\nhttps://www.w3.org/TR/WCAG21/#non-text-content\nhttps://www.w3.org/WAI/tutorials/images/decision-tree/\nhttps://www.w3.org/wai-aria-1.2/#presentation"
|
|
71
|
+
// }),
|
|
72
|
+
// AutoCheckA11yRule.from({
|
|
73
|
+
// reference: "a11y.css",
|
|
74
|
+
// criterion: "Missing source for img",
|
|
75
|
+
// wcag: "",
|
|
76
|
+
// id: "",
|
|
77
|
+
// elementType: "error - no-src",
|
|
78
|
+
// query: new ByTagQuery(["img:not([src], [srcset]), img[src=\"\"], img[src=\" \"], img[src=\"#\"], img[src=\"/\"], img[srcset=\"\"], img[srcset=\" \"], img[srcset=\"#\"], img[srcset=\"/\"], input[type=\"image\"]:not([src], [srcset]), input[type=\"image\"][src=\"\"], input[type=\"image\"][src=\" \"], input[type=\"image\"][src=\"#\"], input[type=\"image\"][src=\"/\"], input[type=\"image\"][srcset=\"\"], input[type=\"image\"][srcset=\" \"], input[type=\"image\"][srcset=\"#\"], input[type=\"image\"][srcset=\"/\"]"]),
|
|
79
|
+
// description: "An <img> must have an [src] or an [srcset], and it should be a valid one. Obviously.",
|
|
80
|
+
// help: "https://html.spec.whatwg.org/multipage/embedded-content.html#attr-img-src\nhttps://scottjehl.github.io/picturefill/"
|
|
81
|
+
// }),
|
|
82
|
+
// AutoCheckA11yRule.from({
|
|
83
|
+
// reference: "a11y.css",
|
|
84
|
+
// criterion: "A label with an empty [for] attribute",
|
|
85
|
+
// wcag: "",
|
|
86
|
+
// id: "",
|
|
87
|
+
// elementType: "error - empty-for",
|
|
88
|
+
// query: new ByTagQuery(["label[for=\"\"], label[for=\" \"]"]),
|
|
89
|
+
// description: "A <label> with a [for] attribute should label something with an [id] attribute, obviously.",
|
|
90
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.1.2\nhttps://github.com/DISIC/rgaa_referentiel_en/blob/44e2bee0c710e37ca49901b1e6b8fae9b553fd5d/criteria.html#L3981\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H44\nhttps://www.w3.org/WAI/tutorials/forms/labels/\nhttps://make.wordpress.org/accessibility/2017/01/16/testing-form-functionality-with-different-assistive-technology/\nhttps://www.w3.org/TR/WCAG21/#labels-or-instructions\nhttps://www.w3.org/TR/WCAG21/#headings-and-labels\nhttps://www.w3.org/TR/WCAG21/#info-and-relationships"
|
|
91
|
+
// }),
|
|
92
|
+
// AutoCheckA11yRule.from({
|
|
93
|
+
// reference: "a11y.css",
|
|
94
|
+
// criterion: "Missing some kind of label",
|
|
95
|
+
// wcag: "",
|
|
96
|
+
// id: "",
|
|
97
|
+
// elementType: "error - no-id",
|
|
98
|
+
// query: new ByTagQuery(["input:not([type=\"button\"], [type=\"submit\"], [type=\"hidden\"], [type=\"reset\"], [type=\"image\"], [id], [aria-label], [title], [aria-labelledby]), textarea:not([id], [aria-label], [aria-labelledby]), select:not([id], [aria-label], [aria-labelledby])"]),
|
|
99
|
+
// description: "How to label a field? You have a few choices: [id], [title], [aria-label], and [aria-labelledby].",
|
|
100
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.1\nhttps://www.w3.org/TR/WCAG21/#labels-or-instructions\nhttps://www.w3.org/TR/WCAG21/#headings-and-labels\nhttps://www.w3.org/TR/WCAG21/#info-and-relationships\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H44"
|
|
101
|
+
// }),
|
|
102
|
+
// AutoCheckA11yRule.from({
|
|
103
|
+
// reference: "a11y.css",
|
|
104
|
+
// criterion: "Missing a value",
|
|
105
|
+
// wcag: "",
|
|
106
|
+
// id: "",
|
|
107
|
+
// elementType: "error - no-value",
|
|
108
|
+
// query: new ByTagQuery(["input[type=\"reset\"]:not([value], [title], [aria-label], [aria-labelledby]), input[type=\"submit\"]:not([value], [title], [aria-label], [aria-labelledby]), input[type=\"button\"]:not([value], [title], [aria-label], [aria-labelledby])"]),
|
|
109
|
+
// description: "How to label a [reset], [submit] or [button] type input? You still have a few choices: [value], [title], [aria-label], and [aria-labelledby].",
|
|
110
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.9\nhttps://www.w3.org/TR/WCAG21/#name-role-value\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H91"
|
|
111
|
+
// }),
|
|
112
|
+
// AutoCheckA11yRule.from({
|
|
113
|
+
// reference: "a11y.css",
|
|
114
|
+
// criterion: "Empty button",
|
|
115
|
+
// wcag: "",
|
|
116
|
+
// id: "",
|
|
117
|
+
// elementType: "error - empty-button",
|
|
118
|
+
// query: new ByTagQuery(["button:empty:not([aria-label], [aria-labelledby], [title])"]),
|
|
119
|
+
// description: "A <button> should either have content or an [aria-label], [aria-labelledby] or [title]. Those attributes, if present, should not be empty.",
|
|
120
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.9\nhttps://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11\nhttps://www.w3.org/TR/WCAG21/#name-role-value\nhttps://www.w3.org/WAI/WCAG21/Techniques/html/H91\nhttps://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L193"
|
|
121
|
+
// }),
|
|
122
|
+
// AutoCheckA11yRule.from({
|
|
123
|
+
// reference: "a11y.css",
|
|
124
|
+
// criterion: "Empty button attribute",
|
|
125
|
+
// wcag: "",
|
|
126
|
+
// id: "",
|
|
127
|
+
// elementType: "error - empty-button-attr",
|
|
128
|
+
// query: new ByTagQuery(["button[title=\"\"], button[aria-label=\"\"], button[aria-labelledby=\"\"]"]),
|
|
129
|
+
// description: "[aria-label], [aria-labelledby] or [title] on a <button> must not be empty.",
|
|
130
|
+
// help: ""
|
|
131
|
+
// }),
|
|
132
|
+
// AutoCheckA11yRule.from({
|
|
133
|
+
// reference: "a11y.css",
|
|
134
|
+
// criterion: "Form button",
|
|
135
|
+
// wcag: "",
|
|
136
|
+
// id: "",
|
|
137
|
+
// elementType: "error - not-form-button",
|
|
138
|
+
// query: new ByTagQuery(["button:not([type], [form], [formaction], [formtarget])"]),
|
|
139
|
+
// description: "A `<button>` has a default `[type]` value of \"submit\", in order to send a `<form>`. However, outside a form, if it's intended to send a form, it should mention it with one of those attributes: `[form]`, `[formaction]`, `[formtarget]`. If it doesn't submit anything, it should have the `[type=\"button\"].",
|
|
140
|
+
// help: "<https://html.spec.whatwg.org/multipage/forms.html#the-button-element>\n<https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L189>"
|
|
141
|
+
// }),
|
|
142
|
+
// AutoCheckA11yRule.from({
|
|
143
|
+
// reference: "a11y.css",
|
|
144
|
+
// criterion: "Button not submitting",
|
|
145
|
+
// wcag: "",
|
|
146
|
+
// id: "",
|
|
147
|
+
// elementType: "error - not-submit-button",
|
|
148
|
+
// query: new ByTagQuery(["button[type=\"reset\"], button[type=\"button\"]"]),
|
|
149
|
+
// description: "If a `<button>`'s `[type]` is either \"reset\" or \"button\", it should not use the following attributes: [formmethod], [formaction], [formtarget], [formenctype], [formnovalidate].",
|
|
150
|
+
// help: "<https://html.spec.whatwg.org/multipage/forms.html#the-button-element>"
|
|
151
|
+
// }),
|
|
152
|
+
// AutoCheckA11yRule.from({
|
|
153
|
+
// reference: "a11y.css",
|
|
154
|
+
// criterion: "Disabled button",
|
|
155
|
+
// wcag: "",
|
|
156
|
+
// id: "",
|
|
157
|
+
// elementType: "error - disabled-button",
|
|
158
|
+
// query: new ByTagQuery(["button[class*=\"disabled\"]:not([disabled], [readonly])"]),
|
|
159
|
+
// description: "A `<button>` styled to be disabled should be disabled *for real*. Use `[disabled]` and `[readonly].",
|
|
160
|
+
// help: "<https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L122>"
|
|
161
|
+
// }),
|
|
162
|
+
// AutoCheckA11yRule.from({
|
|
163
|
+
// reference: "a11y.css",
|
|
164
|
+
// criterion: "input without [type]",
|
|
165
|
+
// wcag: "",
|
|
166
|
+
// id: "",
|
|
167
|
+
// elementType: "error - no-type",
|
|
168
|
+
// query: new ByTagQuery(["input:not([type]), input[type=\" \"], input[type=\"\"]"]),
|
|
169
|
+
// description: "`<input>` needs a `[type]` in order to tell the user what kind of data is wanted.",
|
|
170
|
+
// help: "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.11>\n<https://www.w3.org/TR/WCAG21/#error-suggestion>"
|
|
171
|
+
// }),
|
|
172
|
+
// AutoCheckA11yRule.from({
|
|
173
|
+
// reference: "a11y.css",
|
|
174
|
+
// criterion: "optgroup without label",
|
|
175
|
+
// wcag: "",
|
|
176
|
+
// id: "",
|
|
177
|
+
// elementType: "error - optgroup",
|
|
178
|
+
// query: new ByTagQuery(["optgroup:not([label])"]),
|
|
179
|
+
// description: "`<optgroup>` needs a `[label]` to explain what's inside the group.",
|
|
180
|
+
// help: "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.8>\n<https://www.w3.org/WAI/WCAG21/Techniques/html/H85>\n<https://www.w3.org/TR/2014/NOTE-WCAG20-TECHS-20140916/H85>"
|
|
181
|
+
// }),
|
|
182
|
+
// AutoCheckA11yRule.from({
|
|
183
|
+
// reference: "a11y.css",
|
|
184
|
+
// criterion: "iframe without [title]",
|
|
185
|
+
// wcag: "",
|
|
186
|
+
// id: "",
|
|
187
|
+
// elementType: "error - no-title",
|
|
188
|
+
// query: new ByTagQuery(["iframe:not([title]), iframe[title=\" \"], iframe[title=\"\"]"]),
|
|
189
|
+
// description: "`<iframe>` needs a `[title]` in order to tell the user what to expect inside the iframe.",
|
|
190
|
+
// help: "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#2.1>\n<https://www.w3.org/TR/WCAG21/#name-role-value>\n<https://www.w3.org/WAI/WCAG21/Techniques/html/H64>"
|
|
191
|
+
// }),
|
|
192
|
+
// AutoCheckA11yRule.from({
|
|
193
|
+
// reference: "a11y.css",
|
|
194
|
+
// criterion: "form missing an [action]",
|
|
195
|
+
// wcag: "",
|
|
196
|
+
// id: "",
|
|
197
|
+
// elementType: "error - no-action",
|
|
198
|
+
// query: new ByTagQuery(["form:not([action]), form[action=\" \"], form[action=\"\"]"]),
|
|
199
|
+
// description: "`<form>` should do something, isn't it? Well, `[action]` is meant to define what.",
|
|
200
|
+
// help: "<https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L214>"
|
|
201
|
+
// }),
|
|
202
|
+
// AutoCheckA11yRule.from({
|
|
203
|
+
// reference: "a11y.css",
|
|
204
|
+
// criterion: "No valid language defined",
|
|
205
|
+
// wcag: "",
|
|
206
|
+
// id: "",
|
|
207
|
+
// elementType: "error - no-lang",
|
|
208
|
+
// query: new ByTagQuery(["html:not([lang]), html[lang*=\" \"], html[lang=\"\"]"]),
|
|
209
|
+
// description: "`<html>` must indicate to User Agents the human language used in the document.",
|
|
210
|
+
// help: "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.3>\n<https://accessibilite.numerique.gouv.fr/methode/glossaire/#langue-par-defaut>\n<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.4>\n<https://www.w3.org/TR/WCAG21/#language-of-page>\n<https://www.w3.org/WAI/WCAG21/Techniques/html/H57>\n<https://html.spec.whatwg.org/#attr-lang>\n<https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang>\n<https://www.w3.org/WAI/WCAG21/Understanding/language-of-page>\n<https://www.w3.org/WAI/WCAG21/Understanding/language-of-parts.html>\n<https://www.matuzo.at/blog/lang-attribute/>\n<https://codepen.io/matuzo/project/editor/ZyrVee>"
|
|
211
|
+
// }),
|
|
212
|
+
// AutoCheckA11yRule.from({
|
|
213
|
+
// reference: "a11y.css",
|
|
214
|
+
// criterion: "table used for layout",
|
|
215
|
+
// wcag: "",
|
|
216
|
+
// id: "",
|
|
217
|
+
// elementType: "error - table-for-layout",
|
|
218
|
+
// query: new ByTagQuery(["table[role=\"presentation\"] th, table[role=\"presentation\"] thead, table[role=\"presentation\"] tfoot, table[role=\"presentation\"] caption, table[role=\"presentation\"] colgroup, table[role=\"presentation\"] [axis], table[role=\"presentation\"] [scope], table[role=\"presentation\"] [headers]"]),
|
|
219
|
+
// description: "`<table>` may be used for layout if a [role=\"presentation\"] is added. However, semantics tags and attributes aiming to organize data mustn't be used. Here's a list: <th>, <thead>, <tfoot>, <caption>, [axis], [scope], [headers], [colgroup].",
|
|
220
|
+
// help: "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#5.8>\n<https://www.w3.org/WAI/WCAG21/Techniques/failures/F46>\n<https://www.w3.org/WAI/WCAG21/Techniques/failures/F49>\n<https://www.w3.org/TR/WCAG21/#info-and-relationships>"
|
|
221
|
+
// }),
|
|
222
|
+
// AutoCheckA11yRule.from({
|
|
223
|
+
// reference: "a11y.css",
|
|
224
|
+
// criterion: "[width] & [height] attributes",
|
|
225
|
+
// wcag: "",
|
|
226
|
+
// id: "",
|
|
227
|
+
// elementType: "error - dimensions",
|
|
228
|
+
// query: new ByTagQuery([":not(img, object, embed, svg, canvas)[width], :not(img, object, embed, svg, canvas)[height]"]),
|
|
229
|
+
// description: "`[width]` and `[height]` are presentation information. Therefore, they shouldn't be used in markup, except for `<img>`. Use CSS instead.",
|
|
230
|
+
// help: "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#10>\n<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#10.1>\n<https://www.w3.org/TR/WCAG21/#info-and-relationships>\n<https://www.w3.org/TR/WCAG21/#meaningful-sequence>"
|
|
231
|
+
// }),
|
|
232
|
+
// AutoCheckA11yRule.from({
|
|
233
|
+
// reference: "a11y.css",
|
|
234
|
+
// criterion: "Javascript events attributes",
|
|
235
|
+
// wcag: "",
|
|
236
|
+
// id: "",
|
|
237
|
+
// elementType: "error - js-events",
|
|
238
|
+
// query: new ByTagQuery(["[onafterprint], [onbeforeprint], [onbeforeunload], [onerror], [onhaschange], [onload], [onmessage], [onoffline], [ononline], [onpagehide], [onpageshow], [onpopstate], [onredo], [onresize], [onstorage], [onundo], [onunload], [onblur], [onchage], [oncontextmenu], [onfocus], [onformchange], [onforminput], [oninput], [oninvalid], [onreset], [onselect], [onsubmit], [onkeydown], [onkeypress], [onkeyup], [onclick], [ondblclick], [ondrag], [ondragend], [ondragenter], [ondragleave], [ondragover], [ondragstart], [ondrop], [onmousedown], [onmousemove], [onmouseout], [onmouseover], [onmouseup], [onmousewheel], [onscroll], [onabort], [oncanplay], [oncanplaythrough], [ondurationchange], [onemptied], [onended], [onerror], [onloadeddata], [onloadedmetadata], [onloadstart], [onpause], [onplay], [onplaying], [onprogress], [onratechange], [onreadystatechange], [onseeked], [onseeking], [onstalled], [onsuspend], [ontimeupdate], [onvolumechange], [onwaiting]"]),
|
|
239
|
+
// description: "Javascript event attributes (such as `[onmouseover]`) should not be used. Prefer either CSS pseudo-classes (`:hover`, `:focus`, `:active`, etc.) or JS event listeners.",
|
|
240
|
+
// help: "<https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes>\n<https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener>"
|
|
241
|
+
// }),
|
|
242
|
+
// AutoCheckA11yRule.from({
|
|
243
|
+
// reference: "a11y.css",
|
|
244
|
+
// criterion: "Namespaces",
|
|
245
|
+
// wcag: "",
|
|
246
|
+
// id: "",
|
|
247
|
+
// elementType: "error - namespace",
|
|
248
|
+
// query: new ByTagQuery(["[id^='1'],[id^='2'],[id^='3'],[id^='4'],[id^='5'],[id^='6'],[id^='7'],[id^='8'],[id^='9'],[id^='0'],[id^='--'],[id^='-1'],[id^='-2'],[id^='-3'],[id^='-4'],[id^='-5'],[id^='-6'],[id^='-7'],[id^='-8'],[id^='-9'],[id^='-0'],[class^='1'],[class^='2'],[class^='3'],[class^='4'],[class^='5'],[class^='6'],[class^=''],[class^='8'],[class^='9'],[class^='0'],[class^='--'],[class^='-1'],[class^='-2'],[clas^='-3'],[class^='-4'],[class^='-5'],[class^='-6'],[class^='-7'],[class^='-8'],[class^='-9'],[class^='-0']"]),
|
|
249
|
+
// description: "Did you know that some characters should be avoided as first characters in class names and identifiers? Yep. Digits, two hyphens, or hyphen followed by a digit.",
|
|
250
|
+
// help: [
|
|
251
|
+
// "https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#w3cselgrammar",
|
|
252
|
+
// "https://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/",
|
|
253
|
+
// "https://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/html/tests/css3-modsel-175a.html"
|
|
254
|
+
// ]
|
|
255
|
+
// }),
|
|
256
|
+
// AutoCheckA11yRule.from({
|
|
257
|
+
// reference: "a11y.css",
|
|
258
|
+
// criterion: "Unaccessible viewport attribute",
|
|
259
|
+
// wcag: "",
|
|
260
|
+
// id: "",
|
|
261
|
+
// elementType: "error - unaccessible-viewport",
|
|
262
|
+
// query: new ByTagQuery(["meta[name='viewport'][content*='maximum-scale'], meta[name='viewport'][content*='minimum-scale'], meta[name='viewport'][content*='user-scalable=no']"]),
|
|
263
|
+
// description: "User should be able to zoom in or out the page to improve readability and comfort; this is usually allowed by the viewport <meta>.",
|
|
264
|
+
// help: [
|
|
265
|
+
// "https://bitsofco.de/linting-html-using-css/",
|
|
266
|
+
// "https://dequeuniversity.com/rules/axe/2.1/meta-viewport",
|
|
267
|
+
// "https://www.w3.org/TR/WCAG21/#resize-text"
|
|
268
|
+
// ]
|
|
269
|
+
// }),
|
|
270
|
+
// AutoCheckA11yRule.from({
|
|
271
|
+
// reference: "a11y.css",
|
|
272
|
+
// criterion: "Incorrect charset",
|
|
273
|
+
// wcag: "",
|
|
274
|
+
// id: "",
|
|
275
|
+
// elementType: "error - incorrect-charset",
|
|
276
|
+
// query: new ByTagQuery(["meta[charset]:not([charset='utf-8'], [charset='UTF-8'])"]),
|
|
277
|
+
// description: "Using utf-8 character encoding is recommended by the W3C itself. Supports many languages and can accommodate pages and forms in any mixture of those languages.",
|
|
278
|
+
// help: [
|
|
279
|
+
// "https://bitsofco.de/linting-html-using-css/#incorrectcharacterset",
|
|
280
|
+
// "https://html.spec.whatwg.org/multipage/semantics.html#attr-meta-charset"
|
|
281
|
+
// ]
|
|
282
|
+
// }),
|
|
283
|
+
// AutoCheckA11yRule.from({
|
|
284
|
+
// reference: "a11y.css",
|
|
285
|
+
// criterion: "Charset should come first",
|
|
286
|
+
// wcag: "",
|
|
287
|
+
// id: "",
|
|
288
|
+
// elementType: "error - late-charset",
|
|
289
|
+
// query: new ByTagQuery(["head :first-child:not([charset])"]),
|
|
290
|
+
// description: "The <meta> element declaring the encoding must be inside the <head> element and within the first 1024 bytes of the HTML.",
|
|
291
|
+
// help: "https://bitsofco.de/linting-html-using-css/#incorrectcharacterset"
|
|
292
|
+
// }),
|
|
293
|
+
// AutoCheckA11yRule.from({
|
|
294
|
+
// reference: "a11y.css",
|
|
295
|
+
// criterion: "Invalid [dir] attribute",
|
|
296
|
+
// wcag: "",
|
|
297
|
+
// id: "",
|
|
298
|
+
// elementType: "error - invalid-dir",
|
|
299
|
+
// query: new ByTagQuery(["[dir]:not([dir='rtl'], [dir='ltr'], [dir='auto'])"]),
|
|
300
|
+
// description: "The [dir] attribute only accepts three values: rtl, ltr, and auto.",
|
|
301
|
+
// help: [
|
|
302
|
+
// "https://www.w3.org/International/questions/qa-html-dir",
|
|
303
|
+
// "https://www.w3.org/International/articles/inline-bidi-markup/#dirattribute"
|
|
304
|
+
// ]
|
|
305
|
+
// }),
|
|
306
|
+
// AutoCheckA11yRule.from({
|
|
307
|
+
// reference: "a11y.css",
|
|
308
|
+
// criterion: "[accesskey] is a bad idea",
|
|
309
|
+
// wcag: "",
|
|
310
|
+
// id: "",
|
|
311
|
+
// elementType: "error - accesskey",
|
|
312
|
+
// query: new ByTagQuery(["[accesskey]"]),
|
|
313
|
+
// description: "The [accesskey] attribute is meant to implement site-specific keyboard shortcuts. This is usually a bad idea since keys might be already used by either the operating system, the browser, browser extension, and even user's settings.",
|
|
314
|
+
// help: "https://jkorpela.fi/forms/accesskey.html"
|
|
315
|
+
// }),
|
|
316
|
+
// AutoCheckA11yRule.from({
|
|
317
|
+
// reference: "a11y.css",
|
|
318
|
+
// criterion: "Grouping inputs",
|
|
319
|
+
// wcag: "",
|
|
320
|
+
// id: "",
|
|
321
|
+
// elementType: "error - inputs-group",
|
|
322
|
+
// query: new ByTagQuery(["input[type='radio']:not([name]), input[type='checkbox']:not(:only-of-type, [name])"]),
|
|
323
|
+
// description: "Inputs with a type of radio or checkbox are usually grouped. The [name] attribute is meant to programmatically associate them, thus is needed.",
|
|
324
|
+
// help: "https://www.w3.org/WAI/tutorials/forms/grouping/"
|
|
325
|
+
// }),
|
|
326
|
+
// AutoCheckA11yRule.from({
|
|
327
|
+
// reference: "a11y.css",
|
|
328
|
+
// criterion: "[radio] outside a fieldset",
|
|
329
|
+
// wcag: "",
|
|
330
|
+
// id: "",
|
|
331
|
+
// elementType: "error - radio-group",
|
|
332
|
+
// query: new ByTagQuery(["input[type='radio']"]),
|
|
333
|
+
// description: "Inputs with a type of radio should be grouped by a parent <fieldset>, described by its <legend>.",
|
|
334
|
+
// help: "https://www.w3.org/WAI/tutorials/forms/grouping/#radio-buttons"
|
|
335
|
+
// }),
|
|
336
|
+
// AutoCheckA11yRule.from({
|
|
337
|
+
// reference: "a11y.css",
|
|
338
|
+
// criterion: "[role=slider] missing attributes",
|
|
339
|
+
// wcag: "",
|
|
340
|
+
// id: "",
|
|
341
|
+
// elementType: "error - role-slider",
|
|
342
|
+
// query: new ByTagQuery(["[role='slider']:not([aria-valuemin]), [role='slider']:not([aria-valuemax]), [role='slider']:not([aria-valuenow])"]),
|
|
343
|
+
// description: "[role='slider'] requires a few attributes: [aria-valuemin], [aria-valuemax], [aria-valuenow].",
|
|
344
|
+
// help: "https://www.w3.org/TR/wai-aria-1.2/#slider"
|
|
345
|
+
// }),
|
|
346
|
+
// AutoCheckA11yRule.from({
|
|
347
|
+
// reference: "a11y.css",
|
|
348
|
+
// criterion: "[role=spinbutton] missing attributes",
|
|
349
|
+
// wcag: "",
|
|
350
|
+
// id: "",
|
|
351
|
+
// elementType: "error - role-spinbutton",
|
|
352
|
+
// query: new ByTagQuery(["[role='spinbutton']:not([aria-valuemin]), [role='spinbutton']:not([aria-valuemax]), [role='spinbutton']:not([aria-valuenow])"]),
|
|
353
|
+
// description: "[role='spinbutton'] requires a few attributes: [aria-valuemin], [aria-valuemax], [aria-valuenow].",
|
|
354
|
+
// help: "https://www.w3.org/TR/wai-aria-1.2/#spinbutton"
|
|
355
|
+
// }),
|
|
356
|
+
// AutoCheckA11yRule.from({
|
|
357
|
+
// reference: "a11y.css",
|
|
358
|
+
// criterion: "[role=checkbox] missing state",
|
|
359
|
+
// wcag: "",
|
|
360
|
+
// id: "",
|
|
361
|
+
// elementType: "error - role-checkbox",
|
|
362
|
+
// query: new ByTagQuery(["[role='checkbox']:not([aria-checked])"]),
|
|
363
|
+
// description: "[role='checkbox'] requires an attribute: [aria-checked].",
|
|
364
|
+
// help: "https://www.w3.org/TR/wai-aria-1.2/#checkbox"
|
|
365
|
+
// }),
|
|
366
|
+
// AutoCheckA11yRule.from({
|
|
367
|
+
// reference: "a11y.css",
|
|
368
|
+
// criterion: "[role=combobox] missing [state]",
|
|
369
|
+
// wcag: "",
|
|
370
|
+
// id: "",
|
|
371
|
+
// elementType: "error - role-combobox",
|
|
372
|
+
// query: new ByTagQuery(["[role='combobox']:not([aria-expanded])"]),
|
|
373
|
+
// description: "[role='combobox'] requires an attribute: [aria-expanded].",
|
|
374
|
+
// help: "https://www.w3.org/TR/wai-aria-1.2/#combobox"
|
|
375
|
+
// }),
|
|
376
|
+
// AutoCheckA11yRule.from({
|
|
377
|
+
// reference: "a11y.css",
|
|
378
|
+
// criterion: "[role=scrollbar] required properties",
|
|
379
|
+
// wcag: "",
|
|
380
|
+
// id: "",
|
|
381
|
+
// elementType: "error - role-scrollbar",
|
|
382
|
+
// query: new ByTagQuery(["[role='scrollbar']:not([aria-controls]), [role='scrollbar']:not([aria-valuemin]), [role='scrollbar']:not([aria-valuemax]), [role='scrollbar']:not([aria-valuenow]), [role='scrollbar']:not([aria-orientation])"]),
|
|
383
|
+
// description: "Some properties are required to comply the [role='scrollbar'] pattern: [aria-controls], [aria-valuemin], [aria-valuemax], [aria-valuenow], [aria-orientation].",
|
|
384
|
+
// help: "https://www.w3.org/TR/wai-aria-1.2/#scrollbar"
|
|
385
|
+
// }),
|
|
386
|
+
// AutoCheckA11yRule.from({
|
|
387
|
+
// reference: "a11y.css",
|
|
388
|
+
// criterion: "Nested interactive elements",
|
|
389
|
+
// wcag: "",
|
|
390
|
+
// id: "",
|
|
391
|
+
// elementType: "error - nested-interactive",
|
|
392
|
+
// query: new ByTagQuery(["a a[href], button a[href], a audio[controls], button audio[controls], a video[controls], button video[controls], a button, button button, a details, button details, a embed, button embed, a iframe, button iframe, a img[usemap], button img[usemap], a label, button label, a select, button select, a textarea, button textarea, a input[type]:not([hidden]), button input[type]:not([hidden]), form form, label label, meter meter, progress progress"]),
|
|
393
|
+
// description: "An interactive element should not be contained in another interactive element (e.g. <a> in <button>).",
|
|
394
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2"
|
|
395
|
+
// }),
|
|
396
|
+
// AutoCheckA11yRule.from({
|
|
397
|
+
// reference: "a11y.css",
|
|
398
|
+
// criterion: "Invalid nesting in a list",
|
|
399
|
+
// wcag: "",
|
|
400
|
+
// id: "",
|
|
401
|
+
// elementType: "warning - invalid-list-nesting",
|
|
402
|
+
// query: new ByTagQuery(["ul > :not(li), ol > :not(li), :not(ul, ol) > li"]),
|
|
403
|
+
// description: "The only child allowed in <ul> and <ol> is <li> - and the converse is also true.",
|
|
404
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2"
|
|
405
|
+
// }),
|
|
406
|
+
// AutoCheckA11yRule.from({
|
|
407
|
+
// reference: "a11y.css",
|
|
408
|
+
// criterion: "Invalid sibling in a definition list",
|
|
409
|
+
// wcag: "",
|
|
410
|
+
// id: "",
|
|
411
|
+
// elementType: "warning - invalid-def",
|
|
412
|
+
// query: new ByTagQuery(["dt + :not(dd), :not(dt, dd) + dd"]),
|
|
413
|
+
// description: "<dt> and <dd> should be direct adjacent siblings, and nothing else. Although multiple <dd> may follow a single <dt>.",
|
|
414
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2"
|
|
415
|
+
// }),
|
|
416
|
+
// AutoCheckA11yRule.from({
|
|
417
|
+
// reference: "a11y.css",
|
|
418
|
+
// criterion: "Invalid nesting in a definition list",
|
|
419
|
+
// wcag: "",
|
|
420
|
+
// id: "",
|
|
421
|
+
// elementType: "warning - invalid-def-nesting",
|
|
422
|
+
// query: new ByTagQuery([":not(dl) > dt, :not(dl) > dd, dl > :not(dt, dd, div)"]),
|
|
423
|
+
// description: "<div>, <dt> and <dd> should be direct children of <dl>. Any other imbrication may be a crime somewhere.",
|
|
424
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2"
|
|
425
|
+
// }),
|
|
426
|
+
// AutoCheckA11yRule.from({
|
|
427
|
+
// reference: "a11y.css",
|
|
428
|
+
// criterion: "figcaption outside a figure",
|
|
429
|
+
// wcag: "",
|
|
430
|
+
// id: "",
|
|
431
|
+
// elementType: "warning - invalid-figcaption-nesting",
|
|
432
|
+
// query: new ByTagQuery([":not(figure) > figcaption"]),
|
|
433
|
+
// description: "<figcaption> doesn't make sense outside a <figure>.",
|
|
434
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2"
|
|
435
|
+
// }),
|
|
436
|
+
// AutoCheckA11yRule.from({
|
|
437
|
+
// reference: "a11y.css",
|
|
438
|
+
// criterion: "figure without the group ARIA role",
|
|
439
|
+
// wcag: "",
|
|
440
|
+
// id: "",
|
|
441
|
+
// elementType: "warning - figure-role",
|
|
442
|
+
// query: new ByTagQuery(["figure:not([role=\"group\"])"]),
|
|
443
|
+
// description: "<figure> needs [role=\"group\"] for accessibility reason.",
|
|
444
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#1.9"
|
|
445
|
+
// }),
|
|
446
|
+
// AutoCheckA11yRule.from({
|
|
447
|
+
// reference: "a11y.css",
|
|
448
|
+
// criterion: "Invalid nesting",
|
|
449
|
+
// wcag: "",
|
|
450
|
+
// id: "",
|
|
451
|
+
// elementType: "warning - invalid-nesting",
|
|
452
|
+
// query: new ByTagQuery(["nav main, aside main, footer main, header main, article main, :not(tr) > td, :not(tr) > th, colgroup *:not(col), :not(colgroup) > col, tr > :not(td, th), optgroup > :not(option), :not(select) > optgroup, :not(fieldset) > legend, select > :not(option, optgroup), table > *:not(thead, tfoot, tbody, tr, colgroup, caption), address h1, address h2, address h3, address h4, address h5, address h6, address nav, address aside, address header, address footer, address address, address article, address section"]),
|
|
453
|
+
// description: "Some nestings are forbidden, and do not have their own test case for now.",
|
|
454
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2"
|
|
455
|
+
// }),
|
|
456
|
+
// AutoCheckA11yRule.from({
|
|
457
|
+
// reference: "a11y.css",
|
|
458
|
+
// criterion: "Misplaced div",
|
|
459
|
+
// wcag: "",
|
|
460
|
+
// id: "",
|
|
461
|
+
// elementType: "warning - misplaced-div",
|
|
462
|
+
// query: new ByTagQuery(["b div, i div, q div, em div, abbr div, cite div, code div, span div, small div, label div, strong div"]),
|
|
463
|
+
// description: "Did you know that you shouldn't add a <div> inside any inline element? You could use a <span> instead.",
|
|
464
|
+
// help: "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L326"
|
|
465
|
+
// }),
|
|
466
|
+
// AutoCheckA11yRule.from({
|
|
467
|
+
// reference: "a11y.css",
|
|
468
|
+
// criterion: "Misused sectioning tags",
|
|
469
|
+
// wcag: "",
|
|
470
|
+
// id: "",
|
|
471
|
+
// elementType: "warning - sectioning-tags",
|
|
472
|
+
// query: new ByTagQuery(["aside > aside:first-child, article > aside:first-child, aside > article:first-child, aside > section:first-child, section > section:first-child, article > section:first-child, article > article:first-child"]),
|
|
473
|
+
// description: "<section>, <aside>, <article> are sectioning tags. They must not be used as wrappers!",
|
|
474
|
+
// help: "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L252"
|
|
475
|
+
// }),
|
|
476
|
+
// AutoCheckA11yRule.from({
|
|
477
|
+
// reference: "a11y.css",
|
|
478
|
+
// criterion: "legend must be a fieldset's:first-child",
|
|
479
|
+
// wcag: "",
|
|
480
|
+
// id: "",
|
|
481
|
+
// elementType: "warning - fieldset",
|
|
482
|
+
// query: new ByTagQuery(["fieldset > *:not(legend):first-child, fieldset > legend:not(:first-child)"]),
|
|
483
|
+
// description: "<legend> must be the first child of a <fieldset>.",
|
|
484
|
+
// help: "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L292"
|
|
485
|
+
// }),
|
|
486
|
+
// AutoCheckA11yRule.from({
|
|
487
|
+
// reference: "a11y.css",
|
|
488
|
+
// criterion: "label must wrap an interactive element",
|
|
489
|
+
// wcag: "",
|
|
490
|
+
// id: "",
|
|
491
|
+
// elementType: "warning - label-wrap",
|
|
492
|
+
// query: new ByTagQuery(["label:has(button, details, input, select, textarea, a, area, fieldset, keygen, object, output, progress, video)"]),
|
|
493
|
+
// description: "<label> must wrap an interactive element.",
|
|
494
|
+
// help: "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L321"
|
|
495
|
+
// }),
|
|
496
|
+
// AutoCheckA11yRule.from({
|
|
497
|
+
// reference: "a11y.css",
|
|
498
|
+
// criterion: "[role=presentation] shouldn't be used on image",
|
|
499
|
+
// wcag: "",
|
|
500
|
+
// id: "",
|
|
501
|
+
// elementType: "warning - presentation",
|
|
502
|
+
// query: new ByTagQuery(["img[role=\"presentation\"], svg[role=\"presentation\"], area[role=\"presentation\"], embed[role=\"presentation\"], canvas[role=\"presentation\"], object[role=\"presentation\"]"]),
|
|
503
|
+
// description: "Any decorative image should be marked up with [aria-hidden=\"true\"] (or empty [alt] if <img>). [role=presentation] shall do the trick but at the time of writing, its support is too low compared to empty [alt] or [aria-hidden=true].",
|
|
504
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#1.2"
|
|
505
|
+
// }),
|
|
506
|
+
// AutoCheckA11yRule.from({
|
|
507
|
+
// reference: "a11y.css",
|
|
508
|
+
// criterion: "A role is needed for svg",
|
|
509
|
+
// wcag: "",
|
|
510
|
+
// id: "",
|
|
511
|
+
// elementType: "warning - no-aria-role",
|
|
512
|
+
// query: new ByTagQuery(["svg:not([aria-hidden=\"true\"], [role=\"img\"])"]),
|
|
513
|
+
// description: "Any <svg> should either have [aria-hidden=\"true\"] if decorative, or a [role=\"img\"] if informative.",
|
|
514
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#1.2"
|
|
515
|
+
// }),
|
|
516
|
+
// AutoCheckA11yRule.from({
|
|
517
|
+
// reference: "a11y.css",
|
|
518
|
+
// criterion: "[autoplay] should probably not be used",
|
|
519
|
+
// wcag: "",
|
|
520
|
+
// id: "",
|
|
521
|
+
// elementType: "warning - autoplay",
|
|
522
|
+
// query: new ByTagQuery(["video[autoplay], audio[autoplay]"]),
|
|
523
|
+
// description: "A time-based media like <audio> or <video> should not [autoplay], because it can be quite surprising for the user.",
|
|
524
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#4.10"
|
|
525
|
+
// }),
|
|
526
|
+
// AutoCheckA11yRule.from({
|
|
527
|
+
// reference: "a11y.css",
|
|
528
|
+
// criterion: "[controls] would be helpful",
|
|
529
|
+
// wcag: "",
|
|
530
|
+
// id: "",
|
|
531
|
+
// elementType: "warning - controls",
|
|
532
|
+
// query: new ByTagQuery(["video:not([controls]), audio:not([controls])"]),
|
|
533
|
+
// description: "A time-based media like <audio> or <video> would be easier to use if [controls] are activated for the user.",
|
|
534
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#4.11"
|
|
535
|
+
// }),
|
|
536
|
+
// AutoCheckA11yRule.from({
|
|
537
|
+
// reference: "a11y.css",
|
|
538
|
+
// criterion: "Most of DOM nodes shouldn't be :empty",
|
|
539
|
+
// wcag: "",
|
|
540
|
+
// id: "",
|
|
541
|
+
// elementType: "warning - empty-nodes",
|
|
542
|
+
// query: new ByTagQuery(["body *:empty:not([hidden], [aria-hidden], [src], button, a, iframe, textarea, area, base, br, col, command, embed, hr, img, input, keygen, link, meta, param, source, track, wbr, title)"]),
|
|
543
|
+
// description: "Obviously void elements are empty, as well as <iframe> and <textarea> could be :empty. Any other :empty tag that is not hidden is probably useless, and should be deleted.",
|
|
544
|
+
// help: "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L243"
|
|
545
|
+
// }),
|
|
546
|
+
// AutoCheckA11yRule.from({
|
|
547
|
+
// reference: "a11y.css",
|
|
548
|
+
// criterion: "A single line table may be used for layout",
|
|
549
|
+
// wcag: "",
|
|
550
|
+
// id: "",
|
|
551
|
+
// elementType: "warning - table-layout",
|
|
552
|
+
// query: new ByTagQuery(["table:not([role=\"presentation\"]) > tr:only-child, table:not([role=\"presentation\"]) > tbody > tr:only-child"]),
|
|
553
|
+
// description: "A lonely <tr> can be a symptom of a table used for layout. Should be double checked!",
|
|
554
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#5.3"
|
|
555
|
+
// }),
|
|
556
|
+
// AutoCheckA11yRule.from({
|
|
557
|
+
// reference: "a11y.css",
|
|
558
|
+
// criterion: "Nested tables",
|
|
559
|
+
// wcag: "",
|
|
560
|
+
// id: "",
|
|
561
|
+
// elementType: "warning - nested-table",
|
|
562
|
+
// query: new ByTagQuery(["table table"]),
|
|
563
|
+
// description: "Nested <table> may confuse users. Is it what you meant?",
|
|
564
|
+
// help: "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#5.2"
|
|
565
|
+
// }),
|
|
566
|
+
// AutoCheckA11yRule.from({
|
|
567
|
+
// reference: "a11y.css",
|
|
568
|
+
// criterion: "Obsolete tags in HTML5",
|
|
569
|
+
// wcag: "",
|
|
570
|
+
// id: "",
|
|
571
|
+
// elementType: "obsolete - tags obsolete",
|
|
572
|
+
// query: new ByTagQuery([
|
|
573
|
+
// "applet",
|
|
574
|
+
// "acronym",
|
|
575
|
+
// "bgsound",
|
|
576
|
+
// "dir",
|
|
577
|
+
// "frame",
|
|
578
|
+
// "frameset",
|
|
579
|
+
// "noframes",
|
|
580
|
+
// "hgroup",
|
|
581
|
+
// "isindex",
|
|
582
|
+
// "listing",
|
|
583
|
+
// "nextid",
|
|
584
|
+
// "noembed",
|
|
585
|
+
// "plaintext",
|
|
586
|
+
// "rb",
|
|
587
|
+
// "rtc",
|
|
588
|
+
// "strike",
|
|
589
|
+
// "xmp",
|
|
590
|
+
// "basefont",
|
|
591
|
+
// "big",
|
|
592
|
+
// "blink",
|
|
593
|
+
// "center",
|
|
594
|
+
// "font",
|
|
595
|
+
// "marquee",
|
|
596
|
+
// "multicol",
|
|
597
|
+
// "nobr",
|
|
598
|
+
// "spacer",
|
|
599
|
+
// "tt",
|
|
600
|
+
// "keygen",
|
|
601
|
+
// "menu",
|
|
602
|
+
// "menuitem"
|
|
603
|
+
// ]),
|
|
604
|
+
// description: "Many, many tags are obsolete in HTML5. You should care!",
|
|
605
|
+
// help: [
|
|
606
|
+
// "<https://html.spec.whatwg.org/multipage/obsolete.html#obsolete>",
|
|
607
|
+
// "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2>"
|
|
608
|
+
// ]
|
|
609
|
+
// }),
|
|
610
|
+
// AutoCheckA11yRule.from({
|
|
611
|
+
// reference: "a11y.css",
|
|
612
|
+
// criterion: "Obsolete attributes in HTML5",
|
|
613
|
+
// wcag: "",
|
|
614
|
+
// id: "",
|
|
615
|
+
// elementType: "obsolete - attributes obsolete",
|
|
616
|
+
// query: new ByTagQuery([
|
|
617
|
+
// "[dropzone]",
|
|
618
|
+
// "a[charset], link[charset]",
|
|
619
|
+
// "a[coords]",
|
|
620
|
+
// "a[shape]",
|
|
621
|
+
// "a[methods], link[methods]",
|
|
622
|
+
// "a[name], embed[name], img[name], option[name]",
|
|
623
|
+
// "a[rev], link[rev]",
|
|
624
|
+
// "a[urn], link[urn]",
|
|
625
|
+
// "form[accept]",
|
|
626
|
+
// "area[nohref]",
|
|
627
|
+
// "area[type]",
|
|
628
|
+
// "area[hreflang]",
|
|
629
|
+
// "head[profile]",
|
|
630
|
+
// "html[version]",
|
|
631
|
+
// "input[ismap]",
|
|
632
|
+
// "input[usemap]",
|
|
633
|
+
// "input[inputmode]",
|
|
634
|
+
// "iframe[longdesc], img[longdesc]",
|
|
635
|
+
// "img[lowsrc]",
|
|
636
|
+
// "link[target]",
|
|
637
|
+
// "meta[scheme]",
|
|
638
|
+
// "meta[http-equiv='content-language']",
|
|
639
|
+
// "meta[http-equiv='content-type']",
|
|
640
|
+
// "meta[http-equiv='set-cookie']",
|
|
641
|
+
// "object[archive]",
|
|
642
|
+
// "object[classid]",
|
|
643
|
+
// "object[code]",
|
|
644
|
+
// "object[codebase]",
|
|
645
|
+
// "object[codetype]",
|
|
646
|
+
// "object[declare]",
|
|
647
|
+
// "object[standby]",
|
|
648
|
+
// "param[type]",
|
|
649
|
+
// "param[valuetype]",
|
|
650
|
+
// "script[language]",
|
|
651
|
+
// "script[event]",
|
|
652
|
+
// "script[for]",
|
|
653
|
+
// "table[datapagesize]",
|
|
654
|
+
// "table[summary]",
|
|
655
|
+
// "td[axis], th[axis]",
|
|
656
|
+
// "td[scope]",
|
|
657
|
+
// "td[abbr]",
|
|
658
|
+
// "a[datasrc], applet[datasrc], button[datasrc], div[datasrc], frame[datasrc], iframe[datasrc], img[datasrc], input[datasrc], label[datasrc], legend[datasrc], marquee[datasrc], object[datasrc], option[datasrc], select[datasrc], span[datasrc], table[datasrc], textarea[datasrc]",
|
|
659
|
+
// "a[datafld], applet[datafld], button[datafld], div[datafld], fieldset[datafld], frame[datafld], iframe[datafld], img[datafld], input[datafld], label[datafld], legend[datafld], marquee[datafld], object[datafld], param[datafld], select[datafld], span[datafld], textarea[datafld]",
|
|
660
|
+
// "button[dataformatas], div[dataformatas], input[dataformatas], label[dataformatas], legend[dataformatas], marquee[dataformatas], object[dataformatas], option[dataformatas], select[dataformatas], span[dataformatas], table[dataformatas]",
|
|
661
|
+
// "body[alink]",
|
|
662
|
+
// "body[bgcolor], table[bgcolor], td[bgcolor], th[bgcolor], tr[bgcolor]",
|
|
663
|
+
// "body[link]",
|
|
664
|
+
// "body[marginbottom]",
|
|
665
|
+
// "body[marginheight], iframe[marginheight]",
|
|
666
|
+
// "body[marginleft]",
|
|
667
|
+
// "body[marginright]",
|
|
668
|
+
// "body[margintop]",
|
|
669
|
+
// "body[marginwidth], iframe[marginwidth]",
|
|
670
|
+
// "body[text]",
|
|
671
|
+
// "body[vlink]",
|
|
672
|
+
// "br[clear]",
|
|
673
|
+
// "col[char], tbody[char], thead[char], tfoot[char], td[char], th[char], tr[char]",
|
|
674
|
+
// "col[charoff], tbody[charoff], thead[charoff], tfoot[charoff], td[charoff], th[charoff], tr[charoff]",
|
|
675
|
+
// "col[valign], tbody[valign], thead[valign], tfoot[valign], td[valign], th[valign], tr[valign]",
|
|
676
|
+
// "col[width], hr[width], pre[width], table[width], td[width], th[width]",
|
|
677
|
+
// "dl[compact], ol[compact], ul[compact]",
|
|
678
|
+
// "embed[hspace], iframe[hspace], input[hspace], img[hspace], object[hspace]",
|
|
679
|
+
// "embed[vspace], iframe[vspace], input[vspace], img[vspace], object[vspace]",
|
|
680
|
+
// "hr[color]",
|
|
681
|
+
// "hr[noshade]",
|
|
682
|
+
// "hr[size]",
|
|
683
|
+
// "h1[align], h2[align], h3[align], h4[align], h5[align], h6[align], iframe[align], caption[align], col[align], div[align], embed[align], hr[align], input[align], img[align], legend[align], object[align], p[align], table[align], tbody[align], thead[align], tfoot[align], td[align], th[align], tr[align]",
|
|
684
|
+
// "iframe[allowtransparency]",
|
|
685
|
+
// "iframe[frameborder]",
|
|
686
|
+
// "iframe[framespacing]",
|
|
687
|
+
// "iframe[scrolling]",
|
|
688
|
+
// "img[border], object[border]",
|
|
689
|
+
// "li[type], ul[type]",
|
|
690
|
+
// "table[cellpadding]",
|
|
691
|
+
// "table[cellspacing]",
|
|
692
|
+
// "table[frame]",
|
|
693
|
+
// "table[rules]",
|
|
694
|
+
// "td[height], th[height]",
|
|
695
|
+
// "td[nowrap], th[nowrap]",
|
|
696
|
+
// "body[background], table[background], thead[background], tbody[background], tfoot[background], tr[background], td[background], th[background]",
|
|
697
|
+
// "embed[name], img[name]",
|
|
698
|
+
// "area[nohref]",
|
|
699
|
+
// "area[type]",
|
|
700
|
+
// "area[hreflang]",
|
|
701
|
+
// "input[ismap]",
|
|
702
|
+
// "input[usemap]",
|
|
703
|
+
// "iframe[longdesc], img[longdesc]",
|
|
704
|
+
// "img[lowsrc]",
|
|
705
|
+
// "object[archive]",
|
|
706
|
+
// "object[classid]",
|
|
707
|
+
// "object[code]",
|
|
708
|
+
// "object[codebase]",
|
|
709
|
+
// "object[codetype]",
|
|
710
|
+
// "object[declare]",
|
|
711
|
+
// "object[standby]",
|
|
712
|
+
// "iframe[datasrc], img[datasrc], input[datasrc],object[datasrc], select[datasrc], textarea[datasrc]",
|
|
713
|
+
// "iframe[datafld], img[datafld], input[datafld], object[datafld], select[datafld], textarea[datafld]",
|
|
714
|
+
// "input[dataformatas], object[dataformatas], select[dataformatas]",
|
|
715
|
+
// "iframe[marginheight]",
|
|
716
|
+
// "iframe[marginwidth]",
|
|
717
|
+
// "br[clear]",
|
|
718
|
+
// "hr[width]",
|
|
719
|
+
// "embed[hspace], iframe[hspace], input[hspace], img[hspace], object[hspace]",
|
|
720
|
+
// "embed[vspace], iframe[vspace], input[vspace], img[vspace], object[vspace]",
|
|
721
|
+
// "hr[color]",
|
|
722
|
+
// "hr[noshade]",
|
|
723
|
+
// "hr[size]",
|
|
724
|
+
// "iframe[align], embed[align], hr[align], input[align], img[align], object[align]",
|
|
725
|
+
// "iframe[allowtransparency]",
|
|
726
|
+
// "iframe[frameborder]",
|
|
727
|
+
// "iframe[framespacing]",
|
|
728
|
+
// "iframe[scrolling]",
|
|
729
|
+
// "img[border], object[border]",
|
|
730
|
+
// "input[inputmode]",
|
|
731
|
+
// "link[charset], link[methods]",
|
|
732
|
+
// "link[rev], link[urn]",
|
|
733
|
+
// "link[target]",
|
|
734
|
+
// "meta[scheme]",
|
|
735
|
+
// "meta[http-equiv='content-language']",
|
|
736
|
+
// "meta[http-equiv='content-type']",
|
|
737
|
+
// "meta[http-equiv='set-cookie']",
|
|
738
|
+
// "script[language]",
|
|
739
|
+
// "script[event]",
|
|
740
|
+
// "script[for]"
|
|
741
|
+
// ]),
|
|
742
|
+
// description: "Many, many attributes are obsolete in HTML5. You should care!",
|
|
743
|
+
// help: [
|
|
744
|
+
// "<https://html.spec.whatwg.org/multipage/obsolete.html#obsolete>",
|
|
745
|
+
// "<https://html.spec.whatwg.org/multipage/obsolete.html#non-conforming-features>",
|
|
746
|
+
// "<https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#8.2>",
|
|
747
|
+
// "<https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta>"
|
|
748
|
+
// ]
|
|
749
|
+
// }),
|
|
750
|
+
// AutoCheckA11yRule.from({
|
|
751
|
+
// reference: "a11y.css",
|
|
752
|
+
// criterion: "Empty <select> should start with an empty <option>'s [value]",
|
|
753
|
+
// wcag: "",
|
|
754
|
+
// id: "",
|
|
755
|
+
// elementType: "advice - empty-option-on-select",
|
|
756
|
+
// query: new ByTagQuery(["select[required]:not([multiple])[size='1'], select[required]:not([multiple], [size])"]),
|
|
757
|
+
// description: "A <select required> element, which isn't [multiple] and whose [size] isn't greater than 1, should start with an empty <option>. That being said, you may use a placeholder content for this option but must ensure to use an empty [value] attribute; or set a [size] attribute to the <select>, which value should equal to the number of <option>s.",
|
|
758
|
+
// help: [
|
|
759
|
+
// "https://html.spec.whatwg.org/multipage/form-elements.html#placeholder-label-option",
|
|
760
|
+
// "https://validator.w3.org/nu/?showsource=yes&doc=https%3A%2F%2Fjsbin.com%2Ftozopid%2Fquiet"
|
|
761
|
+
// ]
|
|
762
|
+
// }),
|
|
763
|
+
// AutoCheckA11yRule.from({
|
|
764
|
+
// reference: "a11y.css",
|
|
765
|
+
// criterion: "Empty [class] attribute",
|
|
766
|
+
// wcag: "",
|
|
767
|
+
// id: "",
|
|
768
|
+
// elementType: "advice - empty-class",
|
|
769
|
+
// query: new ByTagQuery(["[class=''], [class=' ']"]),
|
|
770
|
+
// description: "[class] shouldn't be present if empty. Something to improve, right?"
|
|
771
|
+
// }),
|
|
772
|
+
// AutoCheckA11yRule.from({
|
|
773
|
+
// reference: "a11y.css",
|
|
774
|
+
// criterion: "Empty [id] attribute",
|
|
775
|
+
// wcag: "",
|
|
776
|
+
// id: "",
|
|
777
|
+
// elementType: "advice - empty-id",
|
|
778
|
+
// query: new ByTagQuery(["[id=''], [id=' ']"]),
|
|
779
|
+
// description: "[id] shouldn't be present if empty. You'll thank me later."
|
|
780
|
+
// }),
|
|
781
|
+
// AutoCheckA11yRule.from({
|
|
782
|
+
// reference: "a11y.css",
|
|
783
|
+
// criterion: "A second visible main tag",
|
|
784
|
+
// wcag: "",
|
|
785
|
+
// id: "",
|
|
786
|
+
// elementType: "advice - main",
|
|
787
|
+
// query: new ByTagQuery(["main ~ main:not([hidden])"]),
|
|
788
|
+
// description: "A single <main> should be visible at a time. Isn't that obvious?",
|
|
789
|
+
// help: "https://html.spec.whatwg.org/multipage/grouping-content.html#elementdef-main"
|
|
790
|
+
// }),
|
|
791
|
+
// AutoCheckA11yRule.from({
|
|
792
|
+
// reference: "a11y.css",
|
|
793
|
+
// criterion: "A second figcaption tag",
|
|
794
|
+
// wcag: "",
|
|
795
|
+
// id: "",
|
|
796
|
+
// elementType: "advice - figcaption",
|
|
797
|
+
// query: new ByTagQuery(["figcaption:not(:first-of-type)"]),
|
|
798
|
+
// description: "<figcaption> should be single inside its parent. Check the spec!",
|
|
799
|
+
// help: "https://html.spec.whatwg.org/multipage/grouping-content.html#the-figure-element"
|
|
800
|
+
// }),
|
|
801
|
+
// AutoCheckA11yRule.from({
|
|
802
|
+
// reference: "a11y.css",
|
|
803
|
+
// criterion: "figcaption order",
|
|
804
|
+
// wcag: "",
|
|
805
|
+
// id: "",
|
|
806
|
+
// elementType: "advice - figcaption-order",
|
|
807
|
+
// query: new ByTagQuery(["figcaption:not(:first-child, :last-child)"]),
|
|
808
|
+
// description: "<figcaption> should be first or last child. Nothing else.",
|
|
809
|
+
// help: "https://html.spec.whatwg.org/multipage/grouping-content.html#the-figure-element"
|
|
810
|
+
// }),
|
|
811
|
+
// AutoCheckA11yRule.from({
|
|
812
|
+
// reference: "a11y.css",
|
|
813
|
+
// criterion: "An email link should be valid",
|
|
814
|
+
// wcag: "",
|
|
815
|
+
// id: "",
|
|
816
|
+
// elementType: "advice - mailto",
|
|
817
|
+
// query: new ByTagQuery(["[href^='mailto']"]),
|
|
818
|
+
// description: "A <a href='mailto:'> should contain a valid email. Otherwise you may annoy your users."
|
|
819
|
+
// }),
|
|
820
|
+
// AutoCheckA11yRule.from({
|
|
821
|
+
// reference: "a11y.css",
|
|
822
|
+
// criterion: "A phone link should call a real number",
|
|
823
|
+
// wcag: "",
|
|
824
|
+
// id: "",
|
|
825
|
+
// elementType: "advice - tel",
|
|
826
|
+
// query: new ByTagQuery(["[href^='tel']"]),
|
|
827
|
+
// description: "A <a href='tel:'> should contain a valid phone number. Otherwise you may make your users call anybody."
|
|
828
|
+
// }),
|
|
829
|
+
// AutoCheckA11yRule.from({
|
|
830
|
+
// reference: "a11y.css",
|
|
831
|
+
// criterion: "button [role] on a link",
|
|
832
|
+
// wcag: "",
|
|
833
|
+
// id: "",
|
|
834
|
+
// elementType: "advice - role-button",
|
|
835
|
+
// query: new ByTagQuery(["a[role='button']"]),
|
|
836
|
+
// description: "Obviously, a button role on a link is probably not so hard to move to an authentic button tag, isn't it?",
|
|
837
|
+
// help: "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L174"
|
|
838
|
+
// }),
|
|
839
|
+
// AutoCheckA11yRule.from({
|
|
840
|
+
// reference: "a11y.css",
|
|
841
|
+
// criterion: "Link opening new tab",
|
|
842
|
+
// wcag: "",
|
|
843
|
+
// id: "",
|
|
844
|
+
// elementType: "advice - target-blank",
|
|
845
|
+
// query: new ByTagQuery(["[target$='blank']"]),
|
|
846
|
+
// description: "A link opening new tab or window should warn the user about its behavior. You could use a [title], for example.",
|
|
847
|
+
// help: [
|
|
848
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#13.2",
|
|
849
|
+
// "https://www.w3.org/TR/WCAG21/#link-purpose-in-context",
|
|
850
|
+
// "https://www.w3.org/TR/WCAG21/#change-on-request",
|
|
851
|
+
// "https://www.w3.org/WAI/WCAG21/Techniques/html/H33",
|
|
852
|
+
// "https://www.w3.org/WAI/WCAG21/Techniques/html/H83",
|
|
853
|
+
// "https://www.w3.org/WAI/WCAG21/Techniques/failures/F22"
|
|
854
|
+
// ]
|
|
855
|
+
// }),
|
|
856
|
+
// AutoCheckA11yRule.from({
|
|
857
|
+
// reference: "a11y.css",
|
|
858
|
+
// criterion: "Linking to a file",
|
|
859
|
+
// wcag: "",
|
|
860
|
+
// id: "",
|
|
861
|
+
// elementType: "advice - file-format",
|
|
862
|
+
// query: new ByTagQuery(["[download], [href$='.pdf']:not(link), [href$='.doc']:not(link), [href$='.png']:not(link), [href$='.jpg']:not(link), [href$='.gif']:not(link), [href$='.mp3']:not(link), [href$='.mp4']:not(link), [href$='.mov']:not(link), [href$='.ogg']:not(link), [href$='.xls']:not(link), [href$='.txt']:not(link), [href$='.zip']:not(link), [href$='.rar']:not(link), [href$='.docx']:not(link), [href$='.webp']:not(link), [href$='.apng']:not(link), [href$='.svg']:not(link), [href$='.svgz']:not(link)"]),
|
|
863
|
+
// description: "A link to a file should indicate the file format, the file size, and if different from the main document, the file language.",
|
|
864
|
+
// help: [
|
|
865
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#13.3",
|
|
866
|
+
// "https://www.w3.org/TR/WCAG21/#link-purpose-in-context",
|
|
867
|
+
// "https://www.w3.org/WAI/WCAG21/Techniques/html/H33"
|
|
868
|
+
// ]
|
|
869
|
+
// }),
|
|
870
|
+
// AutoCheckA11yRule.from({
|
|
871
|
+
// reference: "a11y.css",
|
|
872
|
+
// criterion: "Duplicated roles",
|
|
873
|
+
// wcag: "",
|
|
874
|
+
// id: "",
|
|
875
|
+
// elementType: "advice - duplicate-roles",
|
|
876
|
+
// query: new ByTagQuery(["[role='main'] ~ [role='main'], [role='search'] ~ [role='search'], [role='banner'] ~ [role='banner'], [role='contentinfo'] ~ [role='contentinfo']"]),
|
|
877
|
+
// description: "Some ARIA roles should be unique: at least [main], [search], [banner], [contentinfo].",
|
|
878
|
+
// help: [
|
|
879
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#12.6",
|
|
880
|
+
// "https://www.w3.org/TR/WCAG21/#bypass-blocks"
|
|
881
|
+
// ]
|
|
882
|
+
// }),
|
|
883
|
+
// AutoCheckA11yRule.from({
|
|
884
|
+
// reference: "a11y.css",
|
|
885
|
+
// criterion: "May have a search [role]?",
|
|
886
|
+
// wcag: "",
|
|
887
|
+
// id: "",
|
|
888
|
+
// elementType: "advice - missing-role",
|
|
889
|
+
// query: new ByTagQuery(["[id*='search']:not([role='search']), [class*='search']:not([role='search'])"]),
|
|
890
|
+
// description: "A [class] or [id] containing 'search' may carry the search ARIA [role].",
|
|
891
|
+
// help: [
|
|
892
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#12.6",
|
|
893
|
+
// "https://www.w3.org/TR/WCAG21/#bypass-blocks"
|
|
894
|
+
// ]
|
|
895
|
+
// }),
|
|
896
|
+
// AutoCheckA11yRule.from({
|
|
897
|
+
// reference: "a11y.css",
|
|
898
|
+
// criterion: "Does this look like [required]?",
|
|
899
|
+
// wcag: "",
|
|
900
|
+
// id: "",
|
|
901
|
+
// elementType: "advice - required",
|
|
902
|
+
// query: new ByTagQuery(["[required], [aria-required]"]),
|
|
903
|
+
// description: "[required] or [aria-required] should be visually understandable.",
|
|
904
|
+
// help: [
|
|
905
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.10",
|
|
906
|
+
// "https://www.w3.org/TR/WCAG21/#labels-or-instructions"
|
|
907
|
+
// ]
|
|
908
|
+
// }),
|
|
909
|
+
// AutoCheckA11yRule.from({
|
|
910
|
+
// reference: "a11y.css",
|
|
911
|
+
// criterion: "Not :empty [hidden] things",
|
|
912
|
+
// wcag: "",
|
|
913
|
+
// id: "",
|
|
914
|
+
// elementType: "advice - hidden",
|
|
915
|
+
// query: new ByTagQuery(["[hidden]:not(:empty), [aria-hidden='true']:not(:empty)"]),
|
|
916
|
+
// description: "[hidden] or [aria-hidden] may not be a good idea if hiding content.",
|
|
917
|
+
// help: [
|
|
918
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#10.8",
|
|
919
|
+
// "https://www.w3.org/TR/WCAG21/#name-role-value"
|
|
920
|
+
// ]
|
|
921
|
+
// }),
|
|
922
|
+
// AutoCheckA11yRule.from({
|
|
923
|
+
// reference: "a11y.css",
|
|
924
|
+
// criterion: "Placeholder can't replace a label",
|
|
925
|
+
// wcag: "",
|
|
926
|
+
// id: "",
|
|
927
|
+
// elementType: "advice - placeholder",
|
|
928
|
+
// query: new ByTagQuery(["[placeholder]:not([title], [aria-label], [aria-labelledby])"]),
|
|
929
|
+
// description: "[placeholder] is not a label, is it?"
|
|
930
|
+
// }),
|
|
931
|
+
// AutoCheckA11yRule.from({
|
|
932
|
+
// reference: "a11y.css",
|
|
933
|
+
// criterion: "video or audio things",
|
|
934
|
+
// wcag: "",
|
|
935
|
+
// id: "",
|
|
936
|
+
// elementType: "advice - video-audio",
|
|
937
|
+
// query: new ByTagQuery(["video, audio"]),
|
|
938
|
+
// description: "A <video> or <audio> needs a few things to be accessible, like a transcript, subtitles, controls. This kind of things.",
|
|
939
|
+
// help: [
|
|
940
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#4.1",
|
|
941
|
+
// "https://www.w3.org/TR/WCAG21/#audio-only-and-video-only-prerecorded",
|
|
942
|
+
// "https://www.w3.org/TR/WCAG21/#audio-description-or-media-alternative-prerecorded",
|
|
943
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#4.13",
|
|
944
|
+
// "https://www.w3.org/TR/WCAG21/#media-alternative-prerecorded"
|
|
945
|
+
// ]
|
|
946
|
+
// }),
|
|
947
|
+
// AutoCheckA11yRule.from({
|
|
948
|
+
// reference: "a11y.css",
|
|
949
|
+
// criterion: "[title] should be equal to alternative",
|
|
950
|
+
// wcag: "",
|
|
951
|
+
// id: "",
|
|
952
|
+
// elementType: "advice - title",
|
|
953
|
+
// query: new ByTagQuery(["img[alt][title], area[alt][title], svg[aria-label][title]"]),
|
|
954
|
+
// description: "A [title] on any graphic content should be equal to [aria-label] or [alt].",
|
|
955
|
+
// help: [
|
|
956
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#1.3",
|
|
957
|
+
// "https://www.w3.org/TR/WCAG21/#non-text-content",
|
|
958
|
+
// "https://www.w3.org/WAI/tutorials/images/decision-tree/",
|
|
959
|
+
// "https://html.spec.whatwg.org/multipage/images.html#alt"
|
|
960
|
+
// ]
|
|
961
|
+
// }),
|
|
962
|
+
// AutoCheckA11yRule.from({
|
|
963
|
+
// reference: "a11y.css",
|
|
964
|
+
// criterion: "Date and time should be understandable",
|
|
965
|
+
// wcag: "",
|
|
966
|
+
// id: "",
|
|
967
|
+
// elementType: "advice - time",
|
|
968
|
+
// query: new ByTagQuery(["time, [datetime]"]),
|
|
969
|
+
// description: "A <time> or [datetime] content should be an understandable and well-formatted date.",
|
|
970
|
+
// help: []
|
|
971
|
+
// }),
|
|
972
|
+
// AutoCheckA11yRule.from({
|
|
973
|
+
// reference: "a11y.css",
|
|
974
|
+
// criterion: "[scope] should be either col or row",
|
|
975
|
+
// wcag: "",
|
|
976
|
+
// id: "",
|
|
977
|
+
// elementType: "advice - th-scope",
|
|
978
|
+
// query: new ByTagQuery(["th[scope]"]),
|
|
979
|
+
// description: "A [scope] can be col or row (and nothing else) and it has to be relevant.",
|
|
980
|
+
// help: [
|
|
981
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#5.7",
|
|
982
|
+
// "https://www.w3.org/TR/WCAG21/#info-and-relationships",
|
|
983
|
+
// "https://www.w3.org/WAI/WCAG21/Techniques/html/H63"
|
|
984
|
+
// ]
|
|
985
|
+
// }),
|
|
986
|
+
// AutoCheckA11yRule.from({
|
|
987
|
+
// reference: "a11y.css",
|
|
988
|
+
// criterion: "Layout table in use",
|
|
989
|
+
// wcag: "",
|
|
990
|
+
// id: "",
|
|
991
|
+
// elementType: "advice - table-presentation",
|
|
992
|
+
// query: new ByTagQuery(["table[role='presentation']"]),
|
|
993
|
+
// description: "A [role='presentation'] is a good idea for any <table> used for layout, but a bad thing for a data table. Double-check this!",
|
|
994
|
+
// help: [
|
|
995
|
+
// "https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#5.3",
|
|
996
|
+
// "https://www.w3.org/TR/WCAG21/#meaningful-sequence"
|
|
997
|
+
// ]
|
|
998
|
+
// }),
|
|
999
|
+
// AutoCheckA11yRule.from({
|
|
1000
|
+
// reference: "a11y.css",
|
|
1001
|
+
// criterion: "No [href]",
|
|
1002
|
+
// wcag: "",
|
|
1003
|
+
// id: "",
|
|
1004
|
+
// elementType: "advice - no-href",
|
|
1005
|
+
// query: new ByTagQuery(["a:not([href])"]),
|
|
1006
|
+
// description: "Yet it's valid to skip the [href] attribute on a link, it's probably better to double-check, isn't it?",
|
|
1007
|
+
// help: [
|
|
1008
|
+
// "https://html.spec.whatwg.org/multipage/links.html#links-created-by-a-and-area-elements",
|
|
1009
|
+
// "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L161",
|
|
1010
|
+
// "https://github.com/Heydon/REVENGE.CSS/blob/master/revenge.css#L165"
|
|
1011
|
+
// ]
|
|
1012
|
+
// }),
|
|
1013
|
+
// AutoCheckA11yRule.from({
|
|
1014
|
+
// reference: "a11y.css",
|
|
1015
|
+
// criterion: "Non HTTPS URL",
|
|
1016
|
+
// wcag: "",
|
|
1017
|
+
// id: "",
|
|
1018
|
+
// elementType: "advice - no-https",
|
|
1019
|
+
// query: new ByTagQuery(["[src^='http:'], [href^='http:']"]),
|
|
1020
|
+
// description: "When possible, try to use HTTPS.",
|
|
1021
|
+
// help: [
|
|
1022
|
+
// "https://transparencyreport.google.com/https/overview?hl=en",
|
|
1023
|
+
// "https://letsencrypt.org/",
|
|
1024
|
+
// "https://checklists.opquast.com/fr/assurance-qualite-web/les-echanges-de-donnees-sensibles-sont-securises-et-signales-comme-tels"
|
|
1025
|
+
// ]
|
|
1026
|
+
// })
|
|
1027
|
+
// ]
|
|
1028
|
+
// };
|