@diegovelasquezweb/a11y-engine 0.2.3 → 0.3.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/assets/discovery/crawler-config.mjs +1 -0
- package/assets/discovery/stack-detection.json +202 -0
- package/assets/discovery/stack-detection.mjs +1 -0
- package/assets/engine/cdp-checks.mjs +1 -0
- package/assets/engine/pa11y-config.mjs +1 -0
- package/assets/remediation/axe-check-maps.mjs +1 -0
- package/assets/remediation/code-patterns.mjs +1 -0
- package/assets/remediation/guardrails.mjs +1 -0
- package/assets/remediation/intelligence.mjs +1 -0
- package/assets/remediation/source-boundaries.mjs +1 -0
- package/assets/reporting/compliance-config.mjs +1 -0
- package/assets/reporting/manual-checks.mjs +1 -0
- package/assets/reporting/wcag-reference.mjs +1 -0
- package/package.json +1 -1
- package/scripts/core/asset-loader.mjs +73 -75
- package/scripts/engine/analyzer.mjs +2 -0
- package/scripts/engine/dom-scanner.mjs +148 -4
- package/scripts/index.mjs +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {"blockedExtensions":["pdf","jpg","jpeg","png","gif","svg","webp","ico","css","js","mjs","json","xml","zip","tar","gz","mp4","mp3","webm","wav","woff","woff2","ttf","otf","eot","avif","csv","txt","map","wasm"],"paginationParams":["page","paged","p","pg","offset","cursor","start","from","skip","limit","per_page"]};
|
|
@@ -29,5 +29,207 @@
|
|
|
29
29
|
["svelte", "svelte"],
|
|
30
30
|
["vue", "vue"],
|
|
31
31
|
["react", "react"]
|
|
32
|
+
],
|
|
33
|
+
"domFrameworkDetectors": [
|
|
34
|
+
{
|
|
35
|
+
"id": "nextjs",
|
|
36
|
+
"type": "framework",
|
|
37
|
+
"signals": [
|
|
38
|
+
{ "kind": "global", "key": "__NEXT_DATA__" },
|
|
39
|
+
{ "kind": "global", "key": "__next" },
|
|
40
|
+
{ "kind": "selector", "value": "script#__NEXT_DATA__" },
|
|
41
|
+
{ "kind": "selector", "value": "div#__next" },
|
|
42
|
+
{ "kind": "scriptSrc", "pattern": "/_next/" },
|
|
43
|
+
{ "kind": "meta", "name": "next-head-count" }
|
|
44
|
+
]
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": "nuxt",
|
|
48
|
+
"type": "framework",
|
|
49
|
+
"signals": [
|
|
50
|
+
{ "kind": "global", "key": "__NUXT__" },
|
|
51
|
+
{ "kind": "global", "key": "$nuxt" },
|
|
52
|
+
{ "kind": "selector", "value": "div#__nuxt" },
|
|
53
|
+
{ "kind": "scriptSrc", "pattern": "/_nuxt/" },
|
|
54
|
+
{ "kind": "meta", "name": "generator", "pattern": "Nuxt" }
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"id": "gatsby",
|
|
59
|
+
"type": "framework",
|
|
60
|
+
"signals": [
|
|
61
|
+
{ "kind": "global", "key": "___gatsby" },
|
|
62
|
+
{ "kind": "selector", "value": "div#___gatsby" },
|
|
63
|
+
{ "kind": "meta", "name": "generator", "pattern": "Gatsby" }
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"id": "angular",
|
|
68
|
+
"type": "framework",
|
|
69
|
+
"signals": [
|
|
70
|
+
{ "kind": "global", "key": "ng" },
|
|
71
|
+
{ "kind": "selector", "value": "[ng-version]" },
|
|
72
|
+
{ "kind": "selector", "value": "app-root" }
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"id": "svelte",
|
|
77
|
+
"type": "framework",
|
|
78
|
+
"signals": [
|
|
79
|
+
{ "kind": "global", "key": "__svelte" },
|
|
80
|
+
{ "kind": "selector", "value": "[data-svelte-h]" },
|
|
81
|
+
{ "kind": "meta", "name": "generator", "pattern": "Svelte" }
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "astro",
|
|
86
|
+
"type": "framework",
|
|
87
|
+
"signals": [
|
|
88
|
+
{ "kind": "selector", "value": "[data-astro-cid]" },
|
|
89
|
+
{ "kind": "meta", "name": "generator", "pattern": "Astro" },
|
|
90
|
+
{ "kind": "selector", "value": "astro-island" }
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "remix",
|
|
95
|
+
"type": "framework",
|
|
96
|
+
"signals": [
|
|
97
|
+
{ "kind": "global", "key": "__remixContext" },
|
|
98
|
+
{ "kind": "global", "key": "__remixManifest" }
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "vue",
|
|
103
|
+
"type": "framework",
|
|
104
|
+
"signals": [
|
|
105
|
+
{ "kind": "global", "key": "__VUE__" },
|
|
106
|
+
{ "kind": "selector", "value": "[data-v-]" },
|
|
107
|
+
{ "kind": "selector", "value": "div#app[data-v-app]" }
|
|
108
|
+
]
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "react",
|
|
112
|
+
"type": "framework",
|
|
113
|
+
"signals": [
|
|
114
|
+
{ "kind": "selector", "value": "[data-reactroot]" },
|
|
115
|
+
{ "kind": "selector", "value": "[data-reactid]" },
|
|
116
|
+
{ "kind": "global", "key": "__REACT_DEVTOOLS_GLOBAL_HOOK__" }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"domCmsDetectors": [
|
|
121
|
+
{
|
|
122
|
+
"id": "wordpress",
|
|
123
|
+
"type": "cms",
|
|
124
|
+
"signals": [
|
|
125
|
+
{ "kind": "meta", "name": "generator", "pattern": "WordPress" },
|
|
126
|
+
{ "kind": "scriptSrc", "pattern": "/wp-content/" },
|
|
127
|
+
{ "kind": "scriptSrc", "pattern": "/wp-includes/" },
|
|
128
|
+
{ "kind": "selector", "value": "link[href*='wp-content']" },
|
|
129
|
+
{ "kind": "selector", "value": "body.wp-site-blocks" }
|
|
130
|
+
]
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"id": "shopify",
|
|
134
|
+
"type": "cms",
|
|
135
|
+
"signals": [
|
|
136
|
+
{ "kind": "global", "key": "Shopify" },
|
|
137
|
+
{ "kind": "scriptSrc", "pattern": "cdn.shopify.com" },
|
|
138
|
+
{ "kind": "meta", "name": "shopify-digital-wallet" },
|
|
139
|
+
{ "kind": "selector", "value": "link[href*='cdn.shopify']" }
|
|
140
|
+
]
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"id": "drupal",
|
|
144
|
+
"type": "cms",
|
|
145
|
+
"signals": [
|
|
146
|
+
{ "kind": "meta", "name": "generator", "pattern": "Drupal" },
|
|
147
|
+
{ "kind": "global", "key": "Drupal" },
|
|
148
|
+
{ "kind": "scriptSrc", "pattern": "/sites/default/files/" }
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"id": "wix",
|
|
153
|
+
"type": "cms",
|
|
154
|
+
"signals": [
|
|
155
|
+
{ "kind": "meta", "name": "generator", "pattern": "Wix" },
|
|
156
|
+
{ "kind": "scriptSrc", "pattern": "static.parastorage.com" },
|
|
157
|
+
{ "kind": "scriptSrc", "pattern": "static.wixstatic.com" }
|
|
158
|
+
]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"id": "squarespace",
|
|
162
|
+
"type": "cms",
|
|
163
|
+
"signals": [
|
|
164
|
+
{ "kind": "meta", "name": "generator", "pattern": "Squarespace" },
|
|
165
|
+
{ "kind": "scriptSrc", "pattern": "static1.squarespace.com" }
|
|
166
|
+
]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
"id": "webflow",
|
|
170
|
+
"type": "cms",
|
|
171
|
+
"signals": [
|
|
172
|
+
{ "kind": "meta", "name": "generator", "pattern": "Webflow" },
|
|
173
|
+
{ "kind": "selector", "value": "html.w-mod-js" },
|
|
174
|
+
{ "kind": "scriptSrc", "pattern": "assets.website-files.com" }
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"id": "joomla",
|
|
179
|
+
"type": "cms",
|
|
180
|
+
"signals": [
|
|
181
|
+
{ "kind": "meta", "name": "generator", "pattern": "Joomla" },
|
|
182
|
+
{ "kind": "scriptSrc", "pattern": "/media/system/js/" }
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"id": "magento",
|
|
187
|
+
"type": "cms",
|
|
188
|
+
"signals": [
|
|
189
|
+
{ "kind": "scriptSrc", "pattern": "/static/version" },
|
|
190
|
+
{ "kind": "selector", "value": "script[data-requiremodule]" },
|
|
191
|
+
{ "kind": "global", "key": "require" }
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
],
|
|
195
|
+
"domUiLibraryDetectors": [
|
|
196
|
+
{
|
|
197
|
+
"id": "bootstrap",
|
|
198
|
+
"signals": [
|
|
199
|
+
{ "kind": "selector", "value": "link[href*='bootstrap']" },
|
|
200
|
+
{ "kind": "scriptSrc", "pattern": "bootstrap" },
|
|
201
|
+
{ "kind": "selector", "value": ".container .row .col" }
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"id": "tailwindcss",
|
|
206
|
+
"signals": [
|
|
207
|
+
{ "kind": "selector", "value": "style[data-precedence]" },
|
|
208
|
+
{ "kind": "selector", "value": "link[href*='tailwind']" },
|
|
209
|
+
{ "kind": "meta", "name": "generator", "pattern": "Tailwind" }
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"id": "material-ui",
|
|
214
|
+
"signals": [
|
|
215
|
+
{ "kind": "selector", "value": "[class*='MuiButton']" },
|
|
216
|
+
{ "kind": "selector", "value": "[class*='MuiTypography']" },
|
|
217
|
+
{ "kind": "selector", "value": "[class*='MuiPaper']" }
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"id": "jquery",
|
|
222
|
+
"signals": [
|
|
223
|
+
{ "kind": "global", "key": "jQuery" },
|
|
224
|
+
{ "kind": "global", "key": "$" }
|
|
225
|
+
]
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
"id": "foundation",
|
|
229
|
+
"signals": [
|
|
230
|
+
{ "kind": "global", "key": "Foundation" },
|
|
231
|
+
{ "kind": "selector", "value": "link[href*='foundation']" }
|
|
232
|
+
]
|
|
233
|
+
}
|
|
32
234
|
]
|
|
33
235
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {"platformStructureDetectors":[["wordpress",["wp-content/themes"]],["drupal",["web/themes","themes"]],["shopify",["sections","snippets","layout","templates"]]],"uiLibraryPackageDetectors":[["@radix-ui","radix"],["@headlessui","headless-ui"],["@chakra-ui","chakra"],["@mantine","mantine"],["@mui","material-ui"],["antd","ant-design"],["@shopify/polaris","polaris"],["@react-aria","react-aria"],["ariakit","ariakit"],["primevue","primevue"],["vuetify","vuetify"],["swiper","swiper"]],"frameworkPackageDetectors":[["next","nextjs"],["gatsby","gatsby"],["nuxt","nuxt"],["@nuxt/core","nuxt"],["@angular/core","angular"],["astro","astro"],["@sveltejs/kit","svelte"],["svelte","svelte"],["vue","vue"],["react","react"]],"domFrameworkDetectors":[{"id":"nextjs","type":"framework","signals":[{"kind":"global","key":"__NEXT_DATA__"},{"kind":"global","key":"__next"},{"kind":"selector","value":"script#__NEXT_DATA__"},{"kind":"selector","value":"div#__next"},{"kind":"scriptSrc","pattern":"/_next/"},{"kind":"meta","name":"next-head-count"}]},{"id":"nuxt","type":"framework","signals":[{"kind":"global","key":"__NUXT__"},{"kind":"global","key":"$nuxt"},{"kind":"selector","value":"div#__nuxt"},{"kind":"scriptSrc","pattern":"/_nuxt/"},{"kind":"meta","name":"generator","pattern":"Nuxt"}]},{"id":"gatsby","type":"framework","signals":[{"kind":"global","key":"___gatsby"},{"kind":"selector","value":"div#___gatsby"},{"kind":"meta","name":"generator","pattern":"Gatsby"}]},{"id":"angular","type":"framework","signals":[{"kind":"global","key":"ng"},{"kind":"selector","value":"[ng-version]"},{"kind":"selector","value":"app-root"}]},{"id":"svelte","type":"framework","signals":[{"kind":"global","key":"__svelte"},{"kind":"selector","value":"[data-svelte-h]"},{"kind":"meta","name":"generator","pattern":"Svelte"}]},{"id":"astro","type":"framework","signals":[{"kind":"selector","value":"[data-astro-cid]"},{"kind":"meta","name":"generator","pattern":"Astro"},{"kind":"selector","value":"astro-island"}]},{"id":"remix","type":"framework","signals":[{"kind":"global","key":"__remixContext"},{"kind":"global","key":"__remixManifest"}]},{"id":"vue","type":"framework","signals":[{"kind":"global","key":"__VUE__"},{"kind":"selector","value":"[data-v-]"},{"kind":"selector","value":"div#app[data-v-app]"}]},{"id":"react","type":"framework","signals":[{"kind":"selector","value":"[data-reactroot]"},{"kind":"selector","value":"[data-reactid]"},{"kind":"global","key":"__REACT_DEVTOOLS_GLOBAL_HOOK__"}]}],"domCmsDetectors":[{"id":"wordpress","type":"cms","signals":[{"kind":"meta","name":"generator","pattern":"WordPress"},{"kind":"scriptSrc","pattern":"/wp-content/"},{"kind":"scriptSrc","pattern":"/wp-includes/"},{"kind":"selector","value":"link[href*='wp-content']"},{"kind":"selector","value":"body.wp-site-blocks"}]},{"id":"shopify","type":"cms","signals":[{"kind":"global","key":"Shopify"},{"kind":"scriptSrc","pattern":"cdn.shopify.com"},{"kind":"meta","name":"shopify-digital-wallet"},{"kind":"selector","value":"link[href*='cdn.shopify']"}]},{"id":"drupal","type":"cms","signals":[{"kind":"meta","name":"generator","pattern":"Drupal"},{"kind":"global","key":"Drupal"},{"kind":"scriptSrc","pattern":"/sites/default/files/"}]},{"id":"wix","type":"cms","signals":[{"kind":"meta","name":"generator","pattern":"Wix"},{"kind":"scriptSrc","pattern":"static.parastorage.com"},{"kind":"scriptSrc","pattern":"static.wixstatic.com"}]},{"id":"squarespace","type":"cms","signals":[{"kind":"meta","name":"generator","pattern":"Squarespace"},{"kind":"scriptSrc","pattern":"static1.squarespace.com"}]},{"id":"webflow","type":"cms","signals":[{"kind":"meta","name":"generator","pattern":"Webflow"},{"kind":"selector","value":"html.w-mod-js"},{"kind":"scriptSrc","pattern":"assets.website-files.com"}]},{"id":"joomla","type":"cms","signals":[{"kind":"meta","name":"generator","pattern":"Joomla"},{"kind":"scriptSrc","pattern":"/media/system/js/"}]},{"id":"magento","type":"cms","signals":[{"kind":"scriptSrc","pattern":"/static/version"},{"kind":"selector","value":"script[data-requiremodule]"},{"kind":"global","key":"require"}]}],"domUiLibraryDetectors":[{"id":"bootstrap","signals":[{"kind":"selector","value":"link[href*='bootstrap']"},{"kind":"scriptSrc","pattern":"bootstrap"},{"kind":"selector","value":".container .row .col"}]},{"id":"tailwindcss","signals":[{"kind":"selector","value":"style[data-precedence]"},{"kind":"selector","value":"link[href*='tailwind']"},{"kind":"meta","name":"generator","pattern":"Tailwind"}]},{"id":"material-ui","signals":[{"kind":"selector","value":"[class*='MuiButton']"},{"kind":"selector","value":"[class*='MuiTypography']"},{"kind":"selector","value":"[class*='MuiPaper']"}]},{"id":"jquery","signals":[{"kind":"global","key":"jQuery"},{"kind":"global","key":"$"}]},{"id":"foundation","signals":[{"kind":"global","key":"Foundation"},{"kind":"selector","value":"link[href*='foundation']"}]}]};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {"interactiveRoles":["button","link","textbox","combobox","listbox","menuitem","tab","checkbox","radio","switch","slider"],"rules":[{"id":"cdp-missing-accessible-name","condition":"interactive-no-name","impact":"serious","tags":["wcag2a","wcag412","cdp-check"],"help":"Interactive elements must have an accessible name","helpUrl":"https://dequeuniversity.com/rules/axe/4.11/button-name","description":"Interactive element with role \"{{role}}\" has no accessible name","failureMessage":"Element with role \"{{role}}\" has no accessible name in the accessibility tree","axeEquivalents":["button-name","link-name","input-name","aria-command-name"]},{"id":"cdp-aria-hidden-focusable","condition":"hidden-focusable","impact":"serious","tags":["wcag2a","wcag412","cdp-check"],"help":"aria-hidden elements must not be focusable","helpUrl":"https://dequeuniversity.com/rules/axe/4.11/aria-hidden-focus","description":"Focusable element with role \"{{role}}\" is aria-hidden","failureMessage":"Focusable element with role \"{{role}}\" is hidden from the accessibility tree","axeEquivalents":["aria-hidden-focus"]}]};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {"ignoreByPrinciple":["Principle1.Guideline1_4.1_4_3.G18.Fail","Principle4.Guideline4_1.4_1_2.H91.A.NoContent"],"impactMap":{"1":"serious","2":"moderate","3":"minor"},"equivalenceMap":{"Principle1.Guideline1_4.1_4_3.G145":"color-contrast","Principle1.Guideline1_4.1_4_3.G18":"color-contrast","Principle1.Guideline1_4.1_4_3.G145.Fail":"color-contrast","Principle1.Guideline1_4.1_4_3.G18.Fail":"color-contrast","Principle1.Guideline1_3.1_3_1.H42":"heading-order","Principle1.Guideline1_3.1_3_1.H42.2":"empty-heading","Principle1.Guideline1_3.1_3_1.H44":"label","Principle1.Guideline1_3.1_3_1.H65":"label","Principle1.Guideline1_3.1_3_1.H71":"label","Principle1.Guideline1_3.1_3_1.H85":"listitem","Principle1.Guideline1_3.1_3_1.H48":"list","Principle1.Guideline1_3.1_3_1.H39":"table-fake-caption","Principle1.Guideline1_3.1_3_1.H73":"table-fake-caption","Principle1.Guideline1_1.1_1_1.H37":"image-alt","Principle1.Guideline1_1.1_1_1.H67":"image-alt","Principle1.Guideline1_1.1_1_1.H36":"input-image-alt","Principle1.Guideline1_1.1_1_1.H2":"image-redundant-alt","Principle1.Guideline1_1.1_1_1.H53":"object-alt","Principle1.Guideline1_1.1_1_1.G94":"image-alt","Principle1.Guideline1_1.1_1_1.H24":"area-alt","Principle2.Guideline2_4.2_4_1.H64":"frame-title","Principle2.Guideline2_4.2_4_1.G1":"bypass","Principle2.Guideline2_4.2_4_1.G124":"bypass","Principle2.Guideline2_4.2_4_2.H25":"document-title","Principle2.Guideline2_4.2_4_4.H77":"link-name","Principle1.Guideline1_1.1_1_1.H30":"link-name","Principle2.Guideline2_4.2_4_6.G197":"label","Principle2.Guideline2_1.2_1_1.G202":"scrollable-region-focusable","Principle3.Guideline3_1.3_1_1.H57":"html-has-lang","Principle3.Guideline3_1.3_1_1.H57.2":"html-has-lang","Principle3.Guideline3_1.3_1_1.H57.3":"html-lang-valid","Principle3.Guideline3_1.3_1_1.H57.3.Lang":"html-lang-valid","Principle3.Guideline3_2.3_2_1.G107":"select-name","Principle3.Guideline3_3.3_3_2.G131":"label","Principle4.Guideline4_1.4_1_1.F77":"duplicate-id","Principle4.Guideline4_1.4_1_2.H91":"button-name","Principle4.Guideline4_1.4_1_2.H91.A":"link-name","Principle4.Guideline4_1.4_1_2.H91.Button":"button-name","Principle4.Guideline4_1.4_1_2.H91.InputText":"label","Principle4.Guideline4_1.4_1_2.H91.Select":"select-name"}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {"failureModes":{"button-has-visible-text":"missing_visible_text","has-visible-text":"missing_visible_text","aria-label":"missing_aria_label","aria-labelledby":"missing_or_invalid_aria_labelledby","explicit-label":"missing_accessible_label","implicit-label":"missing_accessible_label","color-contrast-enhanced":"insufficient_color_contrast","color-contrast":"insufficient_color_contrast","form-field-multiple-labels":"conflicting_label_association","label":"missing_accessible_label","select-name":"missing_select_name","presentational-role":"missing_accessible_name","only-listitems":"invalid_list_structure","listitem":"orphaned_list_item","heading-order":"invalid_heading_order","meta-viewport":"zoom_disabled","region":"content_outside_landmarks"},"relationshipHints":{"aria-labelledby":"aria_labelledby_references_missing_or_invalid_target","explicit-label":"label_not_associated_with_control","implicit-label":"control_has_no_wrapped_label","form-field-multiple-labels":"multiple_labels_reference_same_control","label":"control_has_no_accessible_label_source","select-name":"select_has_no_accessible_label_source","aria-label":"control_has_no_accessible_name_source","presentational-role":"presentational_role_removed_name_source"}};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {"patterns":[{"id":"placeholder-only-label","title":"Input uses placeholder as its only label","severity":"Critical","wcag":"WCAG 1.3.1 / 4.1.2 A","wcag_criterion":"1.3.1","wcag_level":"A","type":"structural","fix_description":"Placeholder text is not a label — it disappears on input and is not reliably announced by screen readers. Fix using one of these approaches:\n\n**Option A — Visually hidden label (preferred when no visible label exists):**\n```jsx\n<label htmlFor=\"field-id\" className=\"sr-only\">Field name</label>\n<input id=\"field-id\" placeholder=\"...\" />\n```\n\n**Option B — aria-label (acceptable for icon-only inputs like search):**\n```jsx\n<input aria-label=\"Search\" placeholder=\"Search\" />\n```\n\n**Option C — Visible label already exists nearby (wire it up):**\n```jsx\n<label htmlFor=\"field-id\">Field name</label>\n<input id=\"field-id\" placeholder=\"...\" />\n```\n\nNever rely on `placeholder` alone as the accessible name. Always verify the label is announced correctly with a screen reader.","requires_manual_verification":true,"regex":"\\bplaceholder=[\"']","globs":["**/*.tsx","**/*.jsx","**/*.html","**/*.vue","**/*.svelte","**/*.astro"],"context_reject_regex":"aria-label|aria-labelledby|for=|htmlFor=|<label","context_window":6},{"id":"mouseover-without-focus","title":"Hover handler has no keyboard focus equivalent","severity":"Serious","wcag":"WCAG 2.1.1 A","wcag_criterion":"2.1.1","wcag_level":"A","type":"structural","fix_description":"Add `onFocus`/`onBlur` (or `onFocusIn`/`onFocusOut`) handlers alongside every `onMouseOver`/`onMouseEnter` handler so keyboard users receive the same interaction.","requires_manual_verification":true,"regex":"onMouseOver=|onMouseEnter=","globs":["**/*.tsx","**/*.jsx","**/*.vue","**/*.svelte","**/*.html"],"context_reject_regex":"onFocus[^I]|onFocusIn","context_window":10},{"id":"new-window-no-warning","title":"Link opens in new tab without warning","severity":"Serious","wcag":"WCAG 3.2.2 A","wcag_criterion":"3.2.2","wcag_level":"A","type":"structural","fix_description":"Add visible text such as `(opens in new tab)` or an `aria-label` containing that warning. Always pair `target=\"_blank\"` with `rel=\"noopener noreferrer\"`.","requires_manual_verification":true,"regex":"target=[\"']_blank[\"']","globs":["**/*.tsx","**/*.jsx","**/*.vue","**/*.svelte","**/*.html","**/*.astro"],"context_reject_regex":"new.tab|new.window|opens.in|sr-only","context_window":5},{"id":"spa-route-title","title":"SPA navigation does not update document.title","severity":"Serious","wcag":"WCAG 2.4.2 A","wcag_criterion":"2.4.2","wcag_level":"A","type":"structural","fix_description":"Set `document.title` after every router navigation call to reflect the new route's page title. Screen reader users rely on the title to know when the page has changed.","requires_manual_verification":true,"regex":"router\\.push\\(|router\\.replace\\(|navigate\\(|useNavigate\\(","globs":["**/*.tsx","**/*.jsx","**/*.ts","**/*.js","**/*.vue"],"context_reject_regex":"document\\.title","context_window":20},{"id":"focus-outline-suppressed","title":"Focus outline suppressed without replacement","severity":"Serious","wcag":"WCAG 2.4.7 AA","wcag_criterion":"2.4.7","wcag_level":"AA","type":"style","fix_description":"Replace `outline: none` / `outline: 0` with a `:focus-visible` rule that renders a clearly visible custom focus indicator (e.g., a 2px solid high-contrast outline). Never suppress focus outlines globally.","requires_manual_verification":true,"regex":"outline:\\s*none|outline:\\s*0|focus:outline-none","globs":["**/*.css","**/*.scss","**/*.sass","**/*.tsx","**/*.jsx"],"context_reject_regex":":focus-visible","context_window":5},{"id":"orientation-lock","title":"Screen orientation locked programmatically","severity":"Moderate","wcag":"WCAG 1.3.4 AA","wcag_criterion":"1.3.4","wcag_level":"AA","type":"structural","fix_description":"Remove the programmatic orientation lock. If a specific orientation is essential for the content, provide an accessible alternative layout for the other orientation.","requires_manual_verification":false,"regex":"screen\\.orientation\\.lock\\(|lockOrientation\\(","globs":["**/*.ts","**/*.js","**/*.tsx","**/*.jsx","**/*.vue","**/*.svelte"],"context_reject_regex":null,"context_window":0},{"id":"character-key-shortcut","title":"Single-character accesskey shortcut with no override mechanism","severity":"Moderate","wcag":"WCAG 2.1.4 A","wcag_criterion":"2.1.4","wcag_level":"A","type":"structural","fix_description":"Remove the `accesskey` attribute or provide a user-facing mechanism to remap or disable it. Single-character shortcuts conflict with screen reader and speech-input keystroke commands.","requires_manual_verification":false,"regex":"\\baccesskey=","globs":["**/*.html","**/*.tsx","**/*.jsx","**/*.vue","**/*.svelte","**/*.astro"],"context_reject_regex":null,"context_window":0}]};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {"shared":["**Element Verification**: Use the **Evidence from DOM** HTML to verify you are editing the correct element before applying any fix. Accessibility fixes are context-sensitive.","**Global Component Check**: If an issue repeats across multiple pages, it is likely inside a Global Component (e.g., `Header.tsx`, `Button.vue`). Fix it once at the source.","**Verification**: After applying a fix, explain *why* it resolves the WCAG criterion mentioned.","**No Placeholders**: Do not add \"todo\" comments. Provide the complete code fix.","**Backend Root Cause**: Some axe findings originate from server-side output (PHP warnings, WP_DEBUG stack traces rendering into the DOM) rather than frontend components. Identify by evidence containing server-generated debug output: PHP warnings, file paths like `wp-config.php` or `.env`, line numbers, or raw error messages in the DOM. For these findings: (1) explain the server-side root cause to the user in plain language, (2) do not attempt any frontend fix, (3) mark as 'Out of scope — backend root cause', (4) exclude from the fix queue and count as unresolved/unactionable in the Step 6 summary.","**Design Tokens (Tailwind)**: When fixing color contrast or visual issues, check `package.json` for the Tailwind version. v3: tokens in `tailwind.config.ts` or `tailwind.config.js`. v4: tokens in `@theme { … }` blocks inside CSS files (e.g. `app/globals.css`); a `tailwind.config.js` with `@config` may also exist — check both. Never report a missing config as an error in v4 projects."],"stack":{"react":"**React Project**: Edit `.tsx`/`.jsx` files in `src/`. **NEVER** edit `dist/`, `build/`, or `.cache/`. Source of Truth: fix at the component, not the compiled output.","nextjs":"**Next.js Project**: Edit `.tsx`/`.jsx` files in `app/`, `pages/`, or `components/`. **NEVER** edit `.next/` or `out/`. Source of Truth: fix at the component or page.","gatsby":"**Gatsby Project**: Edit `.tsx`/`.jsx` in `src/pages/` or `src/components/`. **NEVER** edit `public/` or `.cache/`. Source of Truth: fix at the component.","vue":"**Vue Project**: Edit `.vue` files in `src/`. **NEVER** edit `dist/` or `node_modules/`. Source of Truth: fix at the component.","nuxt":"**Nuxt Project**: Edit `.vue` files in `pages/`, `components/`, or `layouts/`. **NEVER** edit `.nuxt/` or `dist/`. Source of Truth: fix at the component.","angular":"**Angular Project**: Edit `.component.html` and `.component.ts` in `src/`. **NEVER** edit `dist/`. Source of Truth: fix at the component.","astro":"**Astro Project**: Edit `.astro` files in `src/`. **NEVER** edit `dist/` or `.astro/`. Source of Truth: fix at the component or page.","shopify":"**Shopify Project**: Edit `.liquid` files in `sections/`, `snippets/`, or `layout/`. **NEVER** edit compiled assets in `assets/*.min.js` or modify `config/settings_schema.json`. Source of Truth: fix at the template, not the rendered DOM.","wordpress":"**WordPress Project**: Work only in `wp-content/themes/`. **NEVER** edit `wp-content/plugins/`, `wp-admin/`, `wp-includes/`, `wp-config.php`, or any cached file. Source of Truth: fix at the theme template.","svelte":"**Svelte/SvelteKit Project**: Edit `.svelte` files in `src/`. **NEVER** edit `.svelte-kit/`, `build/`, or `dist/`. Source of Truth: fix at the component, not the compiled output.","drupal":"**Drupal Project**: Search for `.html.twig` in `web/themes/` or `themes/`. Clear cache with `drush cr` after changes. **NEVER** edit compiled or cached files.","generic":"**Framework & CMS Awareness**: This project may use React, Vue, Next.js, Astro, **Shopify** (.liquid), **WordPress** (.php), or **Drupal** (.twig).\n - **NEVER** edit compiled, minified, or cached files (e.g., `dist/`, `.next/`, `build/`, `wp-content/cache/`, `assets/*.min.js`).\n - **Source of Truth**: Traced DOM violations must be fixed at the **Source Component** or **Server-side Template**. Edit the origin, not the output."}};
|