eslint-plugin-a11y 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +45 -0
- package/README.md +596 -0
- package/bin/eslint-with-progress.js +119 -0
- package/bin/lint-with-loader.js +143 -0
- package/dist/index.d.mts +64 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +1918 -0
- package/dist/index.mjs +1891 -0
- package/dist/linter/eslint-plugin/formatter-with-progress.d.mts +28 -0
- package/dist/linter/eslint-plugin/formatter-with-progress.d.ts +28 -0
- package/dist/linter/eslint-plugin/formatter-with-progress.js +130 -0
- package/dist/linter/eslint-plugin/formatter-with-progress.mjs +105 -0
- package/dist/linter/eslint-plugin/formatter.d.mts +31 -0
- package/dist/linter/eslint-plugin/formatter.d.ts +31 -0
- package/dist/linter/eslint-plugin/formatter.js +139 -0
- package/dist/linter/eslint-plugin/formatter.mjs +114 -0
- package/dist/linter/eslint-plugin/index.d.mts +24 -0
- package/dist/linter/eslint-plugin/index.d.ts +24 -0
- package/dist/linter/eslint-plugin/index.js +6914 -0
- package/dist/linter/eslint-plugin/index.mjs +6905 -0
- package/package.json +134 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Marlon Maniti
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
## API Reference
|
|
24
|
+
|
|
25
|
+
### A11yChecker
|
|
26
|
+
|
|
27
|
+
#### `check(element: Element): Promise<A11yResults>`
|
|
28
|
+
Performs all accessibility checks on the given element.
|
|
29
|
+
|
|
30
|
+
#### `checkImageAlt(element: Element): A11yViolation[]`
|
|
31
|
+
Checks images for proper alt attributes.
|
|
32
|
+
|
|
33
|
+
#### `checkLinkText(element: Element): A11yViolation[]`
|
|
34
|
+
Validates link text for accessibility.
|
|
35
|
+
|
|
36
|
+
#### `checkButtonLabel(element: Element): A11yViolation[]`
|
|
37
|
+
Ensures buttons have proper labels.
|
|
38
|
+
|
|
39
|
+
#### `checkFormLabels(element: Element): A11yViolation[]`
|
|
40
|
+
Validates form control label associations.
|
|
41
|
+
|
|
42
|
+
#### `checkHeadingOrder(element: Element): A11yViolation[]`
|
|
43
|
+
Checks heading hierarchy.
|
|
44
|
+
|
|
45
|
+
### Types
|
package/README.md
ADDED
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
# eslint-plugin-a11y
|
|
2
|
+
|
|
3
|
+
> **DEPRECATED:** This package has been renamed to [`eslint-plugin-a11y`](https://www.npmjs.com/package/eslint-plugin-a11y).
|
|
4
|
+
> Run: `npm install --save-dev eslint-plugin-a11y`
|
|
5
|
+
> See the [migration guide](./docs/MIGRATION_TO_A11Y.md) for details.
|
|
6
|
+
|
|
7
|
+
> **Catch accessibility issues in your editor, not in production.** Zero-config ESLint plugin + programmatic API for React, Vue, and JSX.
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/eslint-plugin-a11y)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
[](https://www.typescriptlang.org/)
|
|
12
|
+
|
|
13
|
+
## Why a11y?
|
|
14
|
+
|
|
15
|
+
- ✅ **Zero config** - Works out of the box with React, Vue, and JSX
|
|
16
|
+
- ✅ **Real-time feedback** - Catch issues in your editor, not in production
|
|
17
|
+
- ✅ **43 accessibility rules** - Covers images, forms, buttons, landmarks, ARIA, focus, and more
|
|
18
|
+
- ✅ **Editor suggestions** - Get actionable fixes directly in your editor
|
|
19
|
+
- ✅ **Dual API** - Use as ESLint plugin OR programmatic API
|
|
20
|
+
- ✅ **Large project ready** - Minimal preset for incremental adoption
|
|
21
|
+
- ✅ **Framework agnostic** - Works with React, Vue, Preact, Solid, and more
|
|
22
|
+
- ✅ **Fast & lightweight** - Pure AST validation, 35KB bundle, zero memory issues
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install --save-dev eslint-plugin-a11y
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Peer Dependencies
|
|
31
|
+
|
|
32
|
+
- `eslint` (>=8.0.0) - Required for ESLint plugin
|
|
33
|
+
- `vue-eslint-parser` (>=9.0.0) - Optional, only needed for Vue support
|
|
34
|
+
- `jsdom` (>=23.0.0) - Optional, only needed for A11yChecker core library (programmatic API)
|
|
35
|
+
|
|
36
|
+
**Note:** The ESLint plugin does NOT use jsdom. It performs pure AST validation for maximum performance. jsdom is only required if you use the programmatic A11yChecker API in your tests.
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### Option 1: ESLint Plugin (Recommended)
|
|
41
|
+
|
|
42
|
+
Add to your `.eslintrc.js` or `.eslintrc.json`:
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
// .eslintrc.js
|
|
46
|
+
module.exports = {
|
|
47
|
+
plugins: ['a11y'],
|
|
48
|
+
extends: ['plugin:a11y/recommended']
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Or for React/JSX projects:
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
module.exports = {
|
|
56
|
+
plugins: ['a11y'],
|
|
57
|
+
extends: ['plugin:a11y/react']
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
That's it! Start catching accessibility issues immediately in your editor.
|
|
62
|
+
|
|
63
|
+
### Option 2: Programmatic API
|
|
64
|
+
|
|
65
|
+
Use in your tests:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { A11yChecker } from 'eslint-plugin-a11y/core'
|
|
69
|
+
|
|
70
|
+
// Test a DOM element for accessibility violations
|
|
71
|
+
const violations = await A11yChecker.check(element)
|
|
72
|
+
|
|
73
|
+
// Or check specific patterns
|
|
74
|
+
const imageViolations = A11yChecker.checkImageAlt(element)
|
|
75
|
+
const buttonViolations = A11yChecker.checkButtonLabel(element)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## ESLint Plugin vs Programmatic API
|
|
79
|
+
|
|
80
|
+
**Use ESLint Plugin when:**
|
|
81
|
+
- ✅ You want real-time feedback in your editor
|
|
82
|
+
- ✅ You want to catch issues during development
|
|
83
|
+
- ✅ You want CI/CD integration to prevent commits with violations
|
|
84
|
+
- ✅ You want team-wide enforcement
|
|
85
|
+
|
|
86
|
+
**Use Programmatic API when:**
|
|
87
|
+
- ✅ You want to write specific accessibility tests
|
|
88
|
+
- ✅ You need to test dynamically generated DOM
|
|
89
|
+
- ✅ You want custom reporting or analytics
|
|
90
|
+
- ✅ You're writing integration/E2E tests
|
|
91
|
+
|
|
92
|
+
**You can use both!** Many teams use ESLint plugin for development and programmatic API for comprehensive test coverage.
|
|
93
|
+
|
|
94
|
+
## Configuration Options
|
|
95
|
+
|
|
96
|
+
### Available Presets
|
|
97
|
+
|
|
98
|
+
**Classic Config (.eslintrc.js):**
|
|
99
|
+
- `plugin:a11y/minimal` - Only 3 critical rules (best for large projects)
|
|
100
|
+
- `plugin:a11y/recommended` - Balanced approach (default)
|
|
101
|
+
- `plugin:a11y/strict` - All rules as errors
|
|
102
|
+
- `plugin:a11y/react` - Optimized for React/JSX
|
|
103
|
+
- `plugin:a11y/vue` - Optimized for Vue SFC
|
|
104
|
+
|
|
105
|
+
**Flat Config (eslint.config.js) - ESLint v9+:**
|
|
106
|
+
- `flat/recommended` - Rules only (minimal assumptions)
|
|
107
|
+
- `flat/recommended-react` - Rules + React parser
|
|
108
|
+
- `flat/react` - Full React setup
|
|
109
|
+
- `flat/vue` - Full Vue setup
|
|
110
|
+
- `flat/minimal` - Minimal rules only
|
|
111
|
+
- `flat/strict` - All rules as errors
|
|
112
|
+
|
|
113
|
+
### Framework-Specific Setup
|
|
114
|
+
|
|
115
|
+
**React/JSX:**
|
|
116
|
+
```javascript
|
|
117
|
+
module.exports = {
|
|
118
|
+
parser: '@typescript-eslint/parser',
|
|
119
|
+
parserOptions: {
|
|
120
|
+
ecmaFeatures: { jsx: true }
|
|
121
|
+
},
|
|
122
|
+
plugins: ['a11y'],
|
|
123
|
+
extends: ['plugin:a11y/react']
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Vue:**
|
|
128
|
+
```javascript
|
|
129
|
+
module.exports = {
|
|
130
|
+
parser: 'vue-eslint-parser',
|
|
131
|
+
parserOptions: {
|
|
132
|
+
parser: '@typescript-eslint/parser'
|
|
133
|
+
},
|
|
134
|
+
plugins: ['a11y'],
|
|
135
|
+
extends: ['plugin:a11y/vue']
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Ignoring Rules
|
|
140
|
+
|
|
141
|
+
**Ignore a single line:**
|
|
142
|
+
```jsx
|
|
143
|
+
// eslint-disable-next-line a11y/image-alt
|
|
144
|
+
<img src="decorative.jpg" alt="" />
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Ignore an entire file:**
|
|
148
|
+
```jsx
|
|
149
|
+
/* eslint-disable a11y/heading-order */
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Ignore directories:**
|
|
153
|
+
```javascript
|
|
154
|
+
module.exports = {
|
|
155
|
+
plugins: ['a11y'],
|
|
156
|
+
extends: ['plugin:a11y/recommended'],
|
|
157
|
+
ignorePatterns: [
|
|
158
|
+
'**/node_modules/**',
|
|
159
|
+
'**/dist/**',
|
|
160
|
+
'**/*.test.{js,ts,jsx,tsx}'
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Rule Options
|
|
166
|
+
|
|
167
|
+
Many rules support configuration options for fine-tuned control:
|
|
168
|
+
|
|
169
|
+
**image-alt - Decorative images:**
|
|
170
|
+
```javascript
|
|
171
|
+
{
|
|
172
|
+
'a11y/image-alt': ['error', {
|
|
173
|
+
allowMissingAltOnDecorative: true,
|
|
174
|
+
decorativeMatcher: {
|
|
175
|
+
markerAttributes: ['data-decorative']
|
|
176
|
+
}
|
|
177
|
+
}]
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**link-text - Custom denylist:**
|
|
182
|
+
```javascript
|
|
183
|
+
{
|
|
184
|
+
'a11y/link-text': ['warn', {
|
|
185
|
+
denylist: ['click here', 'read more'],
|
|
186
|
+
caseInsensitive: true
|
|
187
|
+
}]
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**heading-order - Skip tolerance:**
|
|
192
|
+
```javascript
|
|
193
|
+
{
|
|
194
|
+
'a11y/heading-order': ['warn', {
|
|
195
|
+
allowSameLevel: true,
|
|
196
|
+
maxSkip: 2
|
|
197
|
+
}]
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
See [Configuration Guide](./docs/CONFIGURATION.md) for all options.
|
|
202
|
+
|
|
203
|
+
### Component Mapping
|
|
204
|
+
|
|
205
|
+
Map your design-system components to native HTML elements:
|
|
206
|
+
|
|
207
|
+
```javascript
|
|
208
|
+
module.exports = {
|
|
209
|
+
plugins: ['a11y'],
|
|
210
|
+
extends: ['plugin:a11y/recommended'],
|
|
211
|
+
settings: {
|
|
212
|
+
'a11y': {
|
|
213
|
+
components: {
|
|
214
|
+
Link: 'a', // Treat <Link> as <a>
|
|
215
|
+
Button: 'button', // Treat <Button> as <button>
|
|
216
|
+
Image: 'img' // Treat <Image> as <img>
|
|
217
|
+
},
|
|
218
|
+
polymorphicPropNames: ['as', 'component'] // Support <Link as="a">
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
Now rules apply to your components:
|
|
225
|
+
```jsx
|
|
226
|
+
<Link href="/about">Click here</Link> // ⚠️ Warning: nonDescriptive
|
|
227
|
+
<Button></Button> // ❌ Error: missingLabel
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Quick start with ESLint flat config (v9+)
|
|
231
|
+
|
|
232
|
+
```js
|
|
233
|
+
// eslint.config.js
|
|
234
|
+
import js from '@eslint/js'
|
|
235
|
+
import tseslint from 'typescript-eslint'
|
|
236
|
+
import react from 'eslint-plugin-react'
|
|
237
|
+
import testA11y from 'eslint-plugin-a11y'
|
|
238
|
+
|
|
239
|
+
export default [
|
|
240
|
+
js.configs.recommended,
|
|
241
|
+
...tseslint.configs.recommended,
|
|
242
|
+
|
|
243
|
+
{
|
|
244
|
+
plugins: {
|
|
245
|
+
react,
|
|
246
|
+
'a11y': testA11y
|
|
247
|
+
},
|
|
248
|
+
languageOptions: {
|
|
249
|
+
parserOptions: {
|
|
250
|
+
ecmaVersion: 2022,
|
|
251
|
+
sourceType: 'module',
|
|
252
|
+
ecmaFeatures: { jsx: true }
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
...testA11y.configs['flat/recommended-react']
|
|
256
|
+
},
|
|
257
|
+
|
|
258
|
+
// Optional: Vue-specific rules only on .vue files
|
|
259
|
+
{
|
|
260
|
+
files: ['**/*.vue'],
|
|
261
|
+
languageOptions: {
|
|
262
|
+
parser: 'vue-eslint-parser',
|
|
263
|
+
parserOptions: {
|
|
264
|
+
parser: '@typescript-eslint/parser',
|
|
265
|
+
ecmaVersion: 2022,
|
|
266
|
+
sourceType: 'module'
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
plugins: {
|
|
270
|
+
'a11y': testA11y
|
|
271
|
+
},
|
|
272
|
+
...testA11y.configs['flat/vue']
|
|
273
|
+
}
|
|
274
|
+
]
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
These presets mirror the classic `.eslintrc` presets and are the easiest way to drop `eslint-plugin-a11y` into a modern ESLint v9+ setup, alongside `@eslint/js`, `typescript-eslint`, and `eslint-plugin-react`.
|
|
278
|
+
|
|
279
|
+
See [Configuration Guide](./docs/CONFIGURATION.md) for more flat-config examples and advanced setups.
|
|
280
|
+
|
|
281
|
+
## Editor Suggestions
|
|
282
|
+
|
|
283
|
+
Many rules provide **suggestions** that appear in your editor, allowing you to quickly fix issues:
|
|
284
|
+
|
|
285
|
+
- **iframe-title**: Suggests adding `title=""` placeholder
|
|
286
|
+
- **button-label**: Suggests adding `aria-label=""` for icon-only buttons
|
|
287
|
+
- **link-text**: Suggests replacing non-descriptive text
|
|
288
|
+
- **heading-order**: Suggests correct heading level
|
|
289
|
+
|
|
290
|
+
In VS Code and other editors with ESLint support, suggestions appear as Quick Fix options (Cmd/Ctrl + .).
|
|
291
|
+
|
|
292
|
+
**Note**: Suggestions are **not** autofixes - they require manual review and approval.
|
|
293
|
+
|
|
294
|
+
## ESLint Rules
|
|
295
|
+
|
|
296
|
+
The plugin provides **43 accessibility rules**:
|
|
297
|
+
|
|
298
|
+
**Core rules:**
|
|
299
|
+
- `a11y/image-alt` - Enforce images, `input[type=image]`, and `area` elements have alt attributes
|
|
300
|
+
- `a11y/button-label` - Enforce buttons have labels
|
|
301
|
+
- `a11y/link-text` - Enforce links have descriptive text
|
|
302
|
+
- `a11y/form-label` - Enforce form controls have labels
|
|
303
|
+
- `a11y/heading-order` - Enforce proper heading hierarchy
|
|
304
|
+
- `a11y/iframe-title` - Enforce iframes have title attributes
|
|
305
|
+
- `a11y/fieldset-legend` - Enforce fieldsets have legend elements
|
|
306
|
+
- `a11y/table-structure` - Enforce tables have proper structure
|
|
307
|
+
- `a11y/details-summary` - Enforce details elements have summary
|
|
308
|
+
- `a11y/video-captions` - Enforce video elements have caption tracks
|
|
309
|
+
- `a11y/audio-captions` - Enforce audio elements have tracks or transcripts
|
|
310
|
+
- `a11y/landmark-roles` - Enforce proper use of landmark elements
|
|
311
|
+
- `a11y/dialog-modal` - Enforce dialog elements have proper accessibility attributes
|
|
312
|
+
- `a11y/aria-validation` - Validate ARIA roles, properties, and ID references (AST-first)
|
|
313
|
+
- `a11y/semantic-html` - Enforce proper use of semantic HTML elements (AST-first)
|
|
314
|
+
- `a11y/form-validation` - Validate form validation patterns (AST-first)
|
|
315
|
+
|
|
316
|
+
**Attribute & document rules:**
|
|
317
|
+
- `a11y/no-access-key` - Disallow accessKey on elements
|
|
318
|
+
- `a11y/no-autofocus` - Disallow autoFocus
|
|
319
|
+
- `a11y/tabindex-no-positive` - Disallow positive tabIndex
|
|
320
|
+
- `a11y/no-distracting-elements` - Disallow blink and marquee
|
|
321
|
+
- `a11y/lang` - Enforce valid lang attribute values
|
|
322
|
+
- `a11y/html-has-lang` - Enforce html element has lang attribute
|
|
323
|
+
|
|
324
|
+
**Focusable & ARIA rules:**
|
|
325
|
+
- `a11y/no-aria-hidden-on-focusable` - Disallow aria-hidden on focusable elements
|
|
326
|
+
- `a11y/no-role-presentation-on-focusable` - Disallow role="presentation" on focusable elements
|
|
327
|
+
- `a11y/no-interactive-element-to-noninteractive-role` - Disallow role="none/presentation" on interactive elements (button, a, input, etc.)
|
|
328
|
+
- `a11y/no-noninteractive-element-to-interactive-role` - Disallow interactive roles on non-interactive elements without keyboard support
|
|
329
|
+
- `a11y/no-redundant-roles` - Disallow explicit roles that match the element's implicit ARIA role
|
|
330
|
+
- `a11y/prefer-tag-over-role` - Prefer semantic native HTML elements over ARIA role attributes on generic elements
|
|
331
|
+
- `a11y/control-has-associated-label` - Enforce ARIA-role interactive controls have an accessible label
|
|
332
|
+
- `a11y/scope` - Enforce valid use of the scope attribute on `<th>` elements
|
|
333
|
+
- `a11y/aria-activedescendant-has-tabindex` - Enforce aria-activedescendant targets are focusable
|
|
334
|
+
|
|
335
|
+
**Event & keyboard rules:**
|
|
336
|
+
- `a11y/click-events-have-key-events` - Enforce onClick has keyboard equivalent
|
|
337
|
+
- `a11y/mouse-events-have-key-events` - Enforce mouse handlers have keyboard equivalent
|
|
338
|
+
- `a11y/no-static-element-interactions` - Disallow static handlers on non-interactive elements
|
|
339
|
+
- `a11y/no-noninteractive-element-interactions` - Disallow interactive handlers on non-interactive elements
|
|
340
|
+
- `a11y/interactive-supports-focus` - Enforce interactive elements are focusable
|
|
341
|
+
- `a11y/no-noninteractive-tabindex` - Disallow tabindex on non-interactive elements
|
|
342
|
+
|
|
343
|
+
**Content & media rules:**
|
|
344
|
+
- `a11y/heading-has-content` - Enforce headings have content
|
|
345
|
+
- `a11y/img-redundant-alt` - Enforce img alt does not contain redundant words
|
|
346
|
+
- `a11y/anchor-ambiguous-text` - Enforce link text is not generic
|
|
347
|
+
- `a11y/anchor-is-valid` - Enforce anchors have valid href (not empty, `#`, or `javascript:`)
|
|
348
|
+
- `a11y/accessible-emoji` - Enforce emoji have accessible labels
|
|
349
|
+
- `a11y/autocomplete-valid` - Enforce autocomplete attribute is valid
|
|
350
|
+
|
|
351
|
+
## Programmatic API
|
|
352
|
+
|
|
353
|
+
### A11yChecker Methods
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
import { A11yChecker } from 'eslint-plugin-a11y/core'
|
|
357
|
+
|
|
358
|
+
// Comprehensive check
|
|
359
|
+
const results = await A11yChecker.check(element)
|
|
360
|
+
|
|
361
|
+
// Individual checks
|
|
362
|
+
A11yChecker.checkImageAlt(element)
|
|
363
|
+
A11yChecker.checkButtonLabel(element)
|
|
364
|
+
A11yChecker.checkLinkText(element)
|
|
365
|
+
A11yChecker.checkFormLabels(element)
|
|
366
|
+
A11yChecker.checkHeadingOrder(element)
|
|
367
|
+
A11yChecker.checkIframeTitle(element)
|
|
368
|
+
A11yChecker.checkFieldsetLegend(element)
|
|
369
|
+
A11yChecker.checkTableStructure(element)
|
|
370
|
+
A11yChecker.checkDetailsSummary(element)
|
|
371
|
+
A11yChecker.checkVideoCaptions(element)
|
|
372
|
+
A11yChecker.checkAudioCaptions(element)
|
|
373
|
+
A11yChecker.checkLandmarks(element)
|
|
374
|
+
A11yChecker.checkDialogModal(element)
|
|
375
|
+
|
|
376
|
+
// Advanced checks (available in core library)
|
|
377
|
+
A11yChecker.checkAriaRoles(element)
|
|
378
|
+
A11yChecker.checkAriaProperties(element)
|
|
379
|
+
A11yChecker.checkAriaRelationships(element)
|
|
380
|
+
A11yChecker.checkAccessibleName(element)
|
|
381
|
+
A11yChecker.checkCompositePatterns(element)
|
|
382
|
+
A11yChecker.checkSemanticHTML(element)
|
|
383
|
+
A11yChecker.checkFormValidationMessages(element)
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Types
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
interface A11yViolation {
|
|
390
|
+
id: string
|
|
391
|
+
description: string
|
|
392
|
+
element: Element
|
|
393
|
+
impact: 'critical' | 'serious' | 'moderate' | 'minor'
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
interface A11yResults {
|
|
397
|
+
violations: A11yViolation[]
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
## Examples
|
|
402
|
+
|
|
403
|
+
### React Component
|
|
404
|
+
|
|
405
|
+
```tsx
|
|
406
|
+
// ❌ ESLint will catch this
|
|
407
|
+
function MyComponent() {
|
|
408
|
+
return (
|
|
409
|
+
<div>
|
|
410
|
+
<img src="photo.jpg" /> {/* Missing alt */}
|
|
411
|
+
<button></button> {/* No label */}
|
|
412
|
+
</div>
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// ✅ Fixed
|
|
417
|
+
function MyComponent() {
|
|
418
|
+
return (
|
|
419
|
+
<div>
|
|
420
|
+
<img src="photo.jpg" alt="A beautiful landscape" />
|
|
421
|
+
<button aria-label="Close menu">×</button>
|
|
422
|
+
</div>
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Vue Component
|
|
428
|
+
|
|
429
|
+
```vue
|
|
430
|
+
<!-- ❌ ESLint will catch this -->
|
|
431
|
+
<template>
|
|
432
|
+
<img src="photo.jpg" />
|
|
433
|
+
<button></button>
|
|
434
|
+
</template>
|
|
435
|
+
|
|
436
|
+
<!-- ✅ Fixed -->
|
|
437
|
+
<template>
|
|
438
|
+
<img src="photo.jpg" alt="A beautiful landscape" />
|
|
439
|
+
<button aria-label="Close menu">×</button>
|
|
440
|
+
</template>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Using in Tests
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
import { render } from '@testing-library/react'
|
|
447
|
+
import { A11yChecker } from 'eslint-plugin-a11y/core'
|
|
448
|
+
|
|
449
|
+
test('component is accessible', async () => {
|
|
450
|
+
const { container } = render(<MyComponent />)
|
|
451
|
+
const results = await A11yChecker.check(container)
|
|
452
|
+
expect(results.violations).toHaveLength(0)
|
|
453
|
+
})
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
## Large Projects
|
|
457
|
+
|
|
458
|
+
For large codebases, start with minimal rules:
|
|
459
|
+
|
|
460
|
+
```javascript
|
|
461
|
+
// .eslintrc.js
|
|
462
|
+
module.exports = {
|
|
463
|
+
plugins: ['a11y'],
|
|
464
|
+
extends: ['plugin:a11y/minimal'],
|
|
465
|
+
ignorePatterns: ['**/node_modules/**', '**/dist/**']
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
See [Large Project Setup Guide](./docs/LARGE_PROJECTS.md) for incremental adoption strategies.
|
|
470
|
+
|
|
471
|
+
## Progress Display (Optional)
|
|
472
|
+
|
|
473
|
+
The plugin includes a progress-aware ESLint wrapper that shows which files are being linted, similar to Vite's test output.
|
|
474
|
+
|
|
475
|
+
### Quick Setup for Next.js Projects
|
|
476
|
+
|
|
477
|
+
Replace `next lint` with the progress wrapper in your `package.json`:
|
|
478
|
+
|
|
479
|
+
```json
|
|
480
|
+
{
|
|
481
|
+
"scripts": {
|
|
482
|
+
"lint": "node node_modules/eslint-plugin-a11y/bin/eslint-with-progress.js",
|
|
483
|
+
"lint:fix": "node node_modules/eslint-plugin-a11y/bin/eslint-with-progress.js --fix"
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Or use the binary directly:
|
|
489
|
+
|
|
490
|
+
```bash
|
|
491
|
+
npx eslint-with-progress
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### What You Get
|
|
495
|
+
|
|
496
|
+
- ✅ **Progress display** - Shows "Linting files..." message
|
|
497
|
+
- ✅ **File-by-file results** with line numbers
|
|
498
|
+
- ✅ **Summary** showing total files, errors, and warnings
|
|
499
|
+
- ✅ **Color-coded** output (errors in red, warnings in yellow)
|
|
500
|
+
- ✅ **Timing information**
|
|
501
|
+
|
|
502
|
+
Example output:
|
|
503
|
+
```
|
|
504
|
+
Linting files...
|
|
505
|
+
|
|
506
|
+
src/components/Button.tsx
|
|
507
|
+
5:12 ✖ Image missing alt attribute (a11y/image-alt)
|
|
508
|
+
8:3 ⚠ Button should have accessible label (a11y/button-label)
|
|
509
|
+
|
|
510
|
+
────────────────────────────────────────────────────────────
|
|
511
|
+
|
|
512
|
+
Summary: 15 files linted • 2 errors • 5 warnings
|
|
513
|
+
|
|
514
|
+
Completed in 1.23s
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
**Note:** This is optional. Your plugin rules work automatically with `next lint` - this just adds progress display.
|
|
518
|
+
|
|
519
|
+
## Performance
|
|
520
|
+
|
|
521
|
+
For large projects, use ESLint caching:
|
|
522
|
+
|
|
523
|
+
```bash
|
|
524
|
+
npx eslint . --cache
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
See [Performance Guide](./docs/PERFORMANCE.md) for optimization tips.
|
|
528
|
+
|
|
529
|
+
## Framework Support
|
|
530
|
+
|
|
531
|
+
- ✅ **React/JSX** - Full support via JSX AST
|
|
532
|
+
- ✅ **Vue** - Full support via vue-eslint-parser
|
|
533
|
+
- ✅ **HTML Strings** - Support for template literals (requires jsdom for core API)
|
|
534
|
+
- ✅ **Any JSX-based framework** - Preact, Solid, etc.
|
|
535
|
+
|
|
536
|
+
## Documentation
|
|
537
|
+
|
|
538
|
+
- [Configuration Guide](./docs/CONFIGURATION.md) - ESLint plugin configuration options, rule options, component mapping
|
|
539
|
+
- [Migration Guide](./docs/MIGRATION_FROM_JSX_A11Y.md) - Migrate from eslint-plugin-jsx-a11y
|
|
540
|
+
- [Large Project Setup Guide](./docs/LARGE_PROJECTS.md) - Incremental adoption strategies
|
|
541
|
+
- [Performance Guide](./docs/PERFORMANCE.md) - Performance optimization tips
|
|
542
|
+
- [ESLint Plugin Guide](./docs/ESLINT_PLUGIN.md) - Complete ESLint plugin documentation
|
|
543
|
+
- [Vue Usage Guide](./docs/VUE_USAGE.md) - Vue-specific setup and examples
|
|
544
|
+
- [Examples](./docs/EXAMPLES.md) - Real-world code examples
|
|
545
|
+
- [Troubleshooting Guide](./docs/TROUBLESHOOTING.md) - Common issues and solutions
|
|
546
|
+
- [JSDOM Guide](./docs/JSDOM.md) - When and how to use jsdom
|
|
547
|
+
|
|
548
|
+
## How It Compares
|
|
549
|
+
|
|
550
|
+
### When to use this vs other a11y tools?
|
|
551
|
+
|
|
552
|
+
- **vs `eslint-plugin-jsx-a11y`**: Similar JSX accessibility coverage, plus Vue SFC support, flat-config presets, and a matching runtime `A11yChecker` API. You can run both plugins side by side and selectively disable overlapping rules in either one.
|
|
553
|
+
- **vs `eslint-plugin-vuejs-accessibility`**: This plugin covers both Vue templates *and* JSX/TSX with a single rule set, which is useful in mixed React/Vue or design-system-heavy codebases.
|
|
554
|
+
- **vs runtime-only tools (e.g. `@axe-core/react`)**: `a11y` focuses on **static**, editor-time feedback and CI linting, while the runtime A11yChecker API complements it for dynamic DOM testing.
|
|
555
|
+
|
|
556
|
+
For rule-by-rule mapping from `eslint-plugin-jsx-a11y` to `eslint-plugin-a11y`, see the [Migration Guide](./docs/MIGRATION_FROM_JSX_A11Y.md).
|
|
557
|
+
|
|
558
|
+
### Feature comparison
|
|
559
|
+
|
|
560
|
+
| Feature | a11y | eslint-plugin-jsx-a11y | @axe-core/react |
|
|
561
|
+
|---------|--------------|------------------------|-----------------|
|
|
562
|
+
| Zero config | ✅ | ❌ | ❌ |
|
|
563
|
+
| Vue support | ✅ | ❌ | ❌ |
|
|
564
|
+
| Programmatic API | ✅ | ❌ | ✅ |
|
|
565
|
+
| Editor integration | ✅ | ✅ | ❌ |
|
|
566
|
+
| Large project ready | ✅ | ⚠️ | ⚠️ |
|
|
567
|
+
| Framework agnostic | ✅ | React only | React only |
|
|
568
|
+
|
|
569
|
+
## FAQ
|
|
570
|
+
|
|
571
|
+
- **Does this replace `eslint-plugin-jsx-a11y`?**
|
|
572
|
+
Not necessarily. It can replace it in many React-only projects, but it also adds Vue SFC support, flat-config presets, and a runtime A11yChecker API. You can also run both plugins side by side and disable overlapping rules where needed.
|
|
573
|
+
|
|
574
|
+
- **Can I run `eslint-plugin-a11y` and `eslint-plugin-jsx-a11y` together?**
|
|
575
|
+
Yes. Add both plugins to your config and then selectively turn off overlapping rules in one or the other. The [Migration Guide](./docs/MIGRATION_FROM_JSX_A11Y.md) shows rule mappings and suggestions.
|
|
576
|
+
|
|
577
|
+
- **Does it support ESLint v9 flat config?**
|
|
578
|
+
Yes. All presets have `flat/*` equivalents (for example, `flat/recommended`, `flat/recommended-react`, `flat/vue`, `flat/minimal`, `flat/strict`). See the flat-config quick start above or the [Configuration Guide](./docs/CONFIGURATION.md).
|
|
579
|
+
|
|
580
|
+
- **Does it work with Vue Single File Components (SFC)?**
|
|
581
|
+
Yes. Install `vue-eslint-parser` and use the `vue` presets (classic or `flat/vue`). The flat-config example above shows how to scope Vue rules to `**/*.vue` files.
|
|
582
|
+
|
|
583
|
+
- **Why does it warn on dynamic `alt`/text instead of erroring?**
|
|
584
|
+
Dynamic attributes (like `alt={altText}`) cannot be fully validated statically. The plugin treats them as warnings by default and expects you to cover them via runtime checks using the A11yChecker API or other testing tools.
|
|
585
|
+
|
|
586
|
+
## Contributing
|
|
587
|
+
|
|
588
|
+
Contributions are welcome! After cloning, `npm install` runs the `prepare` script (build + ContextKit git hooks; hooks live in `.contextkit/hooks/`). Please see the [contributing guidelines](CONTRIBUTING.md) for more information.
|
|
589
|
+
|
|
590
|
+
## Author
|
|
591
|
+
|
|
592
|
+
Marlon Maniti (https://github.com/nolrm)
|
|
593
|
+
|
|
594
|
+
## License
|
|
595
|
+
|
|
596
|
+
MIT
|