@@ -24,11 +24,12 @@ export async function react(
24
24
) : Promise < FlatConfigItem [ ] > {
25
25
const { files, i18n, overrides, typescript, parserOptions } = options ;
26
26
27
- const [ pluginReact , pluginReactHooks , pluginReactRefresh ] = ( await loadPackages ( [
27
+ const [ pluginReact , pluginReactHooks , pluginReactRefresh , pluginJsxA11y ] = ( await loadPackages ( [
28
28
"@eslint-react/eslint-plugin" ,
29
29
"eslint-plugin-react-hooks" ,
30
30
"eslint-plugin-react-refresh" ,
31
- ] ) ) as [ ESLint . Plugin , ESLint . Plugin , ESLint . Plugin ] ;
31
+ "eslint-plugin-jsx-a11y" ,
32
+ ] ) ) as [ ESLint . Plugin , ESLint . Plugin , ESLint . Plugin , ESLint . Plugin ] ;
32
33
33
34
const parserTs = typescript ? await interopDefault ( import ( "@typescript-eslint/parser" ) ) : undefined ;
34
35
@@ -53,6 +54,7 @@ export async function react(
53
54
plugins [ "@eslint-react/naming-convention" ] ??
54
55
assert . fail ( `Failed to find "@eslint-react/naming-convention".` ) ,
55
56
"react-refresh" : pluginReactRefresh ,
57
+ "jsx-a11y" : pluginJsxA11y ,
56
58
} ,
57
59
} ,
58
60
{
@@ -138,6 +140,80 @@ export async function react(
138
140
"react/prefer-shorthand-boolean" : "error" ,
139
141
"react/prefer-shorthand-fragment" : "error" ,
140
142
143
+ "jsx-a11y/alt-text" : "error" ,
144
+ "jsx-a11y/anchor-has-content" : "error" ,
145
+ "jsx-a11y/anchor-is-valid" : "error" ,
146
+ "jsx-a11y/aria-activedescendant-has-tabindex" : "error" ,
147
+ "jsx-a11y/aria-props" : "error" ,
148
+ "jsx-a11y/aria-proptypes" : "error" ,
149
+ "jsx-a11y/aria-role" : "error" ,
150
+ "jsx-a11y/aria-unsupported-elements" : "error" ,
151
+ "jsx-a11y/autocomplete-valid" : "error" ,
152
+ "jsx-a11y/click-events-have-key-events" : "error" ,
153
+ "jsx-a11y/control-has-associated-label" : [
154
+ "off" ,
155
+ {
156
+ ignoreElements : [ "audio" , "canvas" , "embed" , "input" , "textarea" , "tr" , "video" ] ,
157
+ ignoreRoles : [
158
+ "grid" ,
159
+ "listbox" ,
160
+ "menu" ,
161
+ "menubar" ,
162
+ "radiogroup" ,
163
+ "row" ,
164
+ "tablist" ,
165
+ "toolbar" ,
166
+ "tree" ,
167
+ "treegrid" ,
168
+ ] ,
169
+ includeRoles : [ "alert" , "dialog" ] ,
170
+ } ,
171
+ ] ,
172
+ "jsx-a11y/heading-has-content" : "error" ,
173
+ "jsx-a11y/html-has-lang" : "error" ,
174
+ "jsx-a11y/iframe-has-title" : "error" ,
175
+ "jsx-a11y/img-redundant-alt" : "error" ,
176
+ "jsx-a11y/interactive-supports-focus" : [
177
+ "error" ,
178
+ {
179
+ tabbable : [
180
+ "button" ,
181
+ "checkbox" ,
182
+ "link" ,
183
+ "progressbar" ,
184
+ "searchbox" ,
185
+ "slider" ,
186
+ "spinbutton" ,
187
+ "switch" ,
188
+ "textbox" ,
189
+ ] ,
190
+ } ,
191
+ ] ,
192
+ "jsx-a11y/label-has-for" : "off" ,
193
+ "jsx-a11y/label-has-associated-control" : "error" ,
194
+ "jsx-a11y/media-has-caption" : "error" ,
195
+ "jsx-a11y/mouse-events-have-key-events" : "error" ,
196
+ "jsx-a11y/no-access-key" : "error" ,
197
+ "jsx-a11y/no-autofocus" : "error" ,
198
+ "jsx-a11y/no-distracting-elements" : "error" ,
199
+ "jsx-a11y/no-interactive-element-to-noninteractive-role" : "error" ,
200
+ "jsx-a11y/no-noninteractive-element-interactions" : [
201
+ "error" ,
202
+ {
203
+ body : [ "onError" , "onLoad" ] ,
204
+ iframe : [ "onError" , "onLoad" ] ,
205
+ img : [ "onError" , "onLoad" ] ,
206
+ } ,
207
+ ] ,
208
+ "jsx-a11y/no-noninteractive-element-to-interactive-role" : "error" ,
209
+ "jsx-a11y/no-noninteractive-tabindex" : "error" ,
210
+ "jsx-a11y/no-redundant-roles" : "error" ,
211
+ "jsx-a11y/no-static-element-interactions" : "error" ,
212
+ "jsx-a11y/role-has-required-aria-props" : "error" ,
213
+ "jsx-a11y/role-supports-aria-props" : "error" ,
214
+ "jsx-a11y/scope" : "error" ,
215
+ "jsx-a11y/tabindex-no-positive" : "error" ,
216
+
141
217
...( typescript
142
218
? {
143
219
"react/no-leaked-conditional-rendering" : "error" ,
0 commit comments