@a11yfred/neighbor 0.3.0 → 1.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/CHANGELOG.md +59 -7
- package/CONTRIBUTING.md +10 -10
- package/README.md +101 -31
- package/RULES-CONTENT.md +296 -0
- package/RULES-CSS.md +61 -0
- package/RULES-MARKUP.md +156 -0
- package/RULES.md +55 -0
- package/lib/content-rules.js +858 -0
- package/lib/helpers-angular.js +146 -146
- package/lib/helpers-jsx.js +193 -193
- package/lib/helpers-vue.js +151 -151
- package/lib/helpers.js +37 -37
- package/lib/rules.js +2413 -2413
- package/lib/ulam-rules.js +301 -301
- package/neighbor-content.mjs +80 -0
- package/neighbor-eslint-angular.mjs +68 -68
- package/neighbor-eslint-vue.mjs +48 -48
- package/neighbor-eslint.mjs +56 -56
- package/neighbor-stylelint.mjs +257 -256
- package/package.json +18 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,68 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
3
|
+
## 1.0.0 - 2026-05-12
|
|
4
|
+
|
|
5
|
+
### Breaking change
|
|
6
|
+
|
|
7
|
+
CSS rules renamed from `ulam/` to `neighbor/` namespace:
|
|
8
|
+
|
|
9
|
+
| Old | New |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `ulam/user-preferences` | `neighbor/user-preferences` |
|
|
12
|
+
| `ulam/no-outline-none` | `neighbor/no-outline-none` |
|
|
13
|
+
| `ulam/no-forced-colors-none` | `neighbor/no-forced-colors-none` |
|
|
14
|
+
|
|
15
|
+
Update your `.stylelintrc.json` to use the new names.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 0.4.0 - 2026-05-12
|
|
20
|
+
|
|
21
|
+
### New entry point
|
|
22
|
+
|
|
23
|
+
`@a11yfred/neighbor/content` - an ESLint plugin for accessibility and inclusion problems in web and app copy. Lints string literals and JSX text in JS/TS/JSX/TSX files.
|
|
24
|
+
|
|
25
|
+
### New content rules (all `warn`)
|
|
26
|
+
|
|
27
|
+
| Rule | What it flags |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `no-ableist-language` | Slurs, suffering-framing, and condescending euphemisms when writing about disability ("wheelchair-bound", "suffers from", "special needs", "differently abled") |
|
|
30
|
+
| `no-disability-metaphor` | Figurative uses of disability language ("blind spot", "tone deaf", "paralyzed by", "crippling debt") |
|
|
31
|
+
| `no-english-idiom` | English idioms and sports metaphors opaque to ESL and international readers ("slam dunk", "boil the ocean", "circle back", "touch base") |
|
|
32
|
+
| `no-vague-cta` | Vague link and button text ("click here", "read more", "here", "learn more") |
|
|
33
|
+
| `no-directional-language` | Layout-dependent position instructions ("see above", "in the right sidebar", "as shown below") |
|
|
34
|
+
| `no-unexplained-abbreviation` | Acronyms used without a prior expansion in the same file |
|
|
35
|
+
| `no-all-caps-prose` | ALL CAPS words that screen readers may spell out letter-by-letter |
|
|
36
|
+
| `no-vague-error-message` | Error messages that don't explain what went wrong ("An error occurred", "Something went wrong") |
|
|
37
|
+
| `no-ampersand-in-prose` | `&` in place of "and" in prose - announced inconsistently across AT vendors |
|
|
38
|
+
|
|
39
|
+
Rules are synthesised from 17 sources spanning W3C WAI, government plain language guides (US, UK, Australia, Canada), and disability language authorities (NCDJ, AP Stylebook, ADA National Network, APA Style, SIGACCESS). See [RULES-CONTENT.md](RULES-CONTENT.md) for full methodology and source citations.
|
|
40
|
+
|
|
41
|
+
### New rule reference pages
|
|
42
|
+
|
|
43
|
+
RULES.md is now an index. Full references split into:
|
|
44
|
+
|
|
45
|
+
- [RULES-MARKUP.md](RULES-MARKUP.md) - ESLint markup rules
|
|
46
|
+
- [RULES-CSS.md](RULES-CSS.md) - Stylelint CSS rules
|
|
47
|
+
- [RULES-CONTENT.md](RULES-CONTENT.md) - content rules with sources and methodology
|
|
48
|
+
|
|
49
|
+
### Entry point table update
|
|
50
|
+
|
|
51
|
+
`@a11yfred/neighbor/stylelint` added as an explicit stylelint alias alongside the default export.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 0.3.0 - 2026-05-12
|
|
4
56
|
|
|
5
57
|
### New rule
|
|
6
58
|
|
|
7
59
|
| Rule | What it catches |
|
|
8
|
-
|
|
9
|
-
| `
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| `neighbor/no-forced-colors-none` | `forced-color-adjust: none` inside `@media (forced-colors)` - actively opts out of Windows High Contrast Mode |
|
|
10
62
|
|
|
11
63
|
### Severity changes
|
|
12
64
|
|
|
13
|
-
10 rules moved from `warn` to `off` in the recommended config
|
|
65
|
+
10 rules moved from `warn` to `off` in the recommended config - they flag real problems but are too noisy for most codebases by default. All remain available to opt in individually:
|
|
14
66
|
|
|
15
67
|
`no-application-role`, `no-grid-role`, `no-aria-roledescription`, `no-aria-readonly`, `no-tab-without-controls`, `no-href-hash`, `warn-role-alert`, `prefer-aria-disabled`, `no-target-blank-without-label`, `no-dialog-without-close`
|
|
16
68
|
|
|
@@ -25,7 +77,7 @@
|
|
|
25
77
|
|
|
26
78
|
---
|
|
27
79
|
|
|
28
|
-
## 0.2.0
|
|
80
|
+
## 0.2.0 - 2026-05-12
|
|
29
81
|
|
|
30
82
|
### New rules
|
|
31
83
|
|
|
@@ -40,7 +92,7 @@ All four rules run on React, Vue, and Angular.
|
|
|
40
92
|
|
|
41
93
|
### Extended rules
|
|
42
94
|
|
|
43
|
-
**`no-announce-in-render`** now runs in the Vue and Angular plugins, not just React. Safe contexts are tuned per framework
|
|
95
|
+
**`no-announce-in-render`** now runs in the Vue and Angular plugins, not just React. Safe contexts are tuned per framework - Vue recognises `onMounted`, `watch`, `watchEffect`, `nextTick`; Angular recognises `ngOnInit`, `ngAfterViewInit`, `ngOnChanges`, and class method event handlers.
|
|
44
96
|
|
|
45
97
|
### Setup improvements
|
|
46
98
|
|
|
@@ -48,6 +100,6 @@ README now includes correct parser snippets for Vue and Angular, and separate se
|
|
|
48
100
|
|
|
49
101
|
---
|
|
50
102
|
|
|
51
|
-
## 0.1.0
|
|
103
|
+
## 0.1.0 - 2026-04-30
|
|
52
104
|
|
|
53
105
|
Initial release.
|
package/CONTRIBUTING.md
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# Contributing to @a11yfred/neighbor
|
|
2
2
|
|
|
3
|
-
Neighbor is maintained by [@a11yfred](https://github.com/a11yfred). Contributions are welcome from the accessibility community
|
|
3
|
+
Neighbor is maintained by [@a11yfred](https://github.com/a11yfred). Contributions are welcome from the accessibility community - practitioners, AT users, spec readers, and people who have found a gap in existing tooling.
|
|
4
4
|
|
|
5
5
|
## What belongs here
|
|
6
6
|
|
|
7
7
|
A rule belongs in neighbor if it meets all three criteria:
|
|
8
8
|
|
|
9
|
-
1. **Statically detectable**
|
|
10
|
-
2. **Not already covered**
|
|
11
|
-
3. **Expert-backed**
|
|
9
|
+
1. **Statically detectable** - the violation can be identified from markup/code alone, without a browser or AT. Runtime-only failures (color contrast, focus order in the DOM) belong in axe-core.
|
|
10
|
+
2. **Not already covered** - jsx-a11y, vuejs-accessibility, @angular-eslint/template, or axe-core doesn't already flag it in a recommended config.
|
|
11
|
+
3. **Expert-backed** - there's a WCAG SC, ARIA spec citation, or clear consensus from accessibility practitioners (Roselli, O'Hara, Lauke, Sutton, Pickering, Groves, Eggert, etc.).
|
|
12
12
|
|
|
13
13
|
If you're unsure, open an issue before writing a rule. A brief description and a source is enough to start a conversation.
|
|
14
14
|
|
|
15
15
|
## What doesn't belong here
|
|
16
16
|
|
|
17
17
|
- Rules that require runtime information (computed styles, DOM layout, AT output)
|
|
18
|
-
- Rules already in jsx-a11y recommended
|
|
18
|
+
- Rules already in jsx-a11y recommended - neighbor extends it, not replaces it
|
|
19
19
|
- Opinionated style rules without a clear accessibility impact
|
|
20
20
|
- Rules with very high false-positive rates on real codebases (see the rejected rules list in [RULES.md](RULES.md))
|
|
21
21
|
|
|
@@ -27,7 +27,7 @@ cd neighbor
|
|
|
27
27
|
npm install
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
No build step. Rules are plain ES modules
|
|
30
|
+
No build step. Rules are plain ES modules - edit and run ESLint directly.
|
|
31
31
|
|
|
32
32
|
## How rules are structured
|
|
33
33
|
|
|
@@ -62,7 +62,7 @@ The `h` adapter gives you a uniform interface across all three frameworks:
|
|
|
62
62
|
| `h.getElementName(node)` | lowercase tag name, or `null` for custom components |
|
|
63
63
|
| `h.hasAttr(node, name)` | boolean |
|
|
64
64
|
| `h.getRoleValue(node)` | role string or `null` |
|
|
65
|
-
| `h.hasAccessibleName(node)` | boolean
|
|
65
|
+
| `h.hasAccessibleName(node)` | boolean - checks `aria-label` / `aria-labelledby` |
|
|
66
66
|
| `h.isInteractiveElement(node)` | boolean |
|
|
67
67
|
| `h.getParent(node)` | parent element node or `null` |
|
|
68
68
|
| `h.getAncestors(node)` | iterable of ancestor element nodes, root-ward |
|
|
@@ -71,7 +71,7 @@ The `h` adapter gives you a uniform interface across all three frameworks:
|
|
|
71
71
|
| `h.elementVisitor` | AST node type string for `create()` visitor key |
|
|
72
72
|
| `h.elementWithChildrenVisitor` | visitor key for rules that need child access |
|
|
73
73
|
|
|
74
|
-
**Angular caveat:** `getParent()` and `getAncestors()` return `null`/nothing for Angular
|
|
74
|
+
**Angular caveat:** `getParent()` and `getAncestors()` return `null`/nothing for Angular - the template parser doesn't attach parent pointers. Rules that require ancestor walking should degrade gracefully (skip the check, don't throw).
|
|
75
75
|
|
|
76
76
|
After writing your factory:
|
|
77
77
|
|
|
@@ -85,7 +85,7 @@ Stylelint rules live in [`neighbor-stylelint.mjs`](neighbor-stylelint.mjs) and u
|
|
|
85
85
|
|
|
86
86
|
| Severity | When to use |
|
|
87
87
|
|---|---|
|
|
88
|
-
| `error` | Unambiguous AT breakage
|
|
88
|
+
| `error` | Unambiguous AT breakage - a phantom control, broken name computation, HTML spec violation. No legitimate override. |
|
|
89
89
|
| `warn` | Strong guidance with a clear accessibility basis, but real codebases occasionally have justified exceptions. |
|
|
90
90
|
| `off` | Real problem, but fires too often on legitimate patterns to be on by default. Make it available; let teams opt in. |
|
|
91
91
|
|
|
@@ -107,7 +107,7 @@ Use the PR template. The key things:
|
|
|
107
107
|
|
|
108
108
|
- **What problem does this flag?** Link a WCAG SC, ARIA spec section, or expert source.
|
|
109
109
|
- **Why can't axe-core catch it at runtime instead?** (If it can, it probably belongs there.)
|
|
110
|
-
- **What are the false-positive cases?** Be honest
|
|
110
|
+
- **What are the false-positive cases?** Be honest - we'd rather move a rule to `off` than reject it.
|
|
111
111
|
- **Does it degrade gracefully for Angular?** (Parent walking unavailable.)
|
|
112
112
|
|
|
113
113
|
## Questions
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Neighbor is an accessibility linting plugin for ESLint and Stylelint that builds on jsx-a11y. It looks to cover gaps: bad ARIA patterns, live region misuse, missing names on roles, and CSS that removes focus indicators. It also brings that coverage to Vue and Angular, where jsx-a11y does not apply.
|
|
4
4
|
|
|
5
|
-
Some rules are specific to **@ulam**
|
|
5
|
+
Some rules are specific to **@ulam** - an upcoming JavaScript framework by the same author. Those rules are prefixed `no-announce-in-render`, `no-hash-router-in-remix`, and `no-use-page-title-in-remix`. They activate only when @ulam-related imports are detected and are harmless in non-@ulam projects.
|
|
6
6
|
|
|
7
7
|
## Contents
|
|
8
8
|
|
|
@@ -15,13 +15,15 @@ Some rules are specific to **@ulam** — an upcoming JavaScript framework by the
|
|
|
15
15
|
- [Vue](#vue)
|
|
16
16
|
- [Angular](#angular)
|
|
17
17
|
- [Stylelint](#stylelint)
|
|
18
|
+
- [Content linting](#content-linting)
|
|
18
19
|
- [Peer dependencies](#peer-dependencies)
|
|
19
20
|
- [What neighbor adds](#what-neighbor-adds)
|
|
20
|
-
- [ESLint
|
|
21
|
-
- [ESLint
|
|
22
|
-
- [ESLint
|
|
23
|
-
- [ESLint
|
|
24
|
-
- [Stylelint
|
|
21
|
+
- [ESLint - React / JSX](#eslint--react--jsx)
|
|
22
|
+
- [ESLint - Remix 2](#eslint--remix-2)
|
|
23
|
+
- [ESLint - Vue SFCs](#eslint--vue-sfcs)
|
|
24
|
+
- [ESLint - Angular templates](#eslint--angular-templates)
|
|
25
|
+
- [Stylelint - CSS](#stylelint--css)
|
|
26
|
+
- [Content linter](#content-linter)
|
|
25
27
|
- [Rule severity](#rule-severity)
|
|
26
28
|
- [Contributing](CONTRIBUTING.md)
|
|
27
29
|
- [See also](#see-also)
|
|
@@ -37,10 +39,12 @@ npm install --save-dev @a11yfred/neighbor
|
|
|
37
39
|
|
|
38
40
|
| Import | Use for |
|
|
39
41
|
| --- | --- |
|
|
40
|
-
| `@a11yfred/neighbor/eslint` | React / JSX, Remix 2 |
|
|
41
|
-
| `@a11yfred/neighbor/eslint-vue` | Vue SFCs |
|
|
42
|
-
| `@a11yfred/neighbor/eslint-angular` | Angular templates |
|
|
43
|
-
| `@a11yfred/neighbor` |
|
|
42
|
+
| `@a11yfred/neighbor/eslint` | React / JSX, Remix 2 - markup rules |
|
|
43
|
+
| `@a11yfred/neighbor/eslint-vue` | Vue SFCs - markup rules |
|
|
44
|
+
| `@a11yfred/neighbor/eslint-angular` | Angular templates - markup rules |
|
|
45
|
+
| `@a11yfred/neighbor/content` | Any JS/TS/JSX/TSX - content and prose rules |
|
|
46
|
+
| `@a11yfred/neighbor` | Stylelint - CSS rules (default export) |
|
|
47
|
+
| `@a11yfred/neighbor/stylelint` | Stylelint - CSS rules (explicit) |
|
|
44
48
|
|
|
45
49
|
## Setup
|
|
46
50
|
|
|
@@ -87,7 +91,7 @@ export default [
|
|
|
87
91
|
|
|
88
92
|
### Remix 3
|
|
89
93
|
|
|
90
|
-
Remix 3 is framework-agnostic and does not require React. Neighbor does not have a dedicated Remix 3 entry point
|
|
94
|
+
Remix 3 is framework-agnostic and does not require React. Neighbor does not have a dedicated Remix 3 entry point - use the entry point that matches your renderer.
|
|
91
95
|
|
|
92
96
|
If you are using React with Remix 3:
|
|
93
97
|
|
|
@@ -159,23 +163,67 @@ export default [
|
|
|
159
163
|
|
|
160
164
|
### Stylelint
|
|
161
165
|
|
|
162
|
-
```
|
|
166
|
+
```json
|
|
163
167
|
// .stylelintrc.json
|
|
164
168
|
{
|
|
165
169
|
"plugins": ["@a11yfred/neighbor"],
|
|
166
170
|
"rules": {
|
|
167
|
-
"
|
|
168
|
-
"
|
|
171
|
+
"neighbor/user-preferences": true,
|
|
172
|
+
"neighbor/no-outline-none": true,
|
|
173
|
+
"neighbor/no-forced-colors-none": true
|
|
169
174
|
}
|
|
170
175
|
}
|
|
171
176
|
```
|
|
172
177
|
|
|
178
|
+
### Content linting
|
|
179
|
+
|
|
180
|
+
The content plugin lints string literals and JSX text in JavaScript, TypeScript, JSX, and TSX files. It is separate from the markup plugins and can be used alongside any of them.
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
npm install --save-dev @a11yfred/neighbor
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
// eslint.config.js
|
|
188
|
+
import neighborContent from '@a11yfred/neighbor/content'
|
|
189
|
+
|
|
190
|
+
export default [
|
|
191
|
+
{
|
|
192
|
+
files: ['**/*.{js,jsx,ts,tsx}'],
|
|
193
|
+
plugins: { ...neighborContent.configs.recommended.plugins },
|
|
194
|
+
rules: { ...neighborContent.configs.recommended.rules },
|
|
195
|
+
},
|
|
196
|
+
]
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
To use alongside the React markup plugin:
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
// eslint.config.js
|
|
203
|
+
import neighbor from '@a11yfred/neighbor/eslint'
|
|
204
|
+
import neighborContent from '@a11yfred/neighbor/content'
|
|
205
|
+
|
|
206
|
+
export default [
|
|
207
|
+
{
|
|
208
|
+
files: ['**/*.{js,jsx,ts,tsx}'],
|
|
209
|
+
plugins: {
|
|
210
|
+
...neighbor.configs.recommended.plugins,
|
|
211
|
+
...neighborContent.configs.recommended.plugins,
|
|
212
|
+
},
|
|
213
|
+
rules: {
|
|
214
|
+
...neighbor.configs.recommended.rules,
|
|
215
|
+
...neighborContent.configs.recommended.rules,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
]
|
|
219
|
+
```
|
|
220
|
+
|
|
173
221
|
## Peer dependencies
|
|
174
222
|
|
|
175
223
|
| Peer | Required for |
|
|
176
224
|
| --- | --- |
|
|
177
225
|
| `eslint >= 8` | Any ESLint entry point |
|
|
178
|
-
| `eslint-plugin-jsx-a11y >= 6` | React config
|
|
226
|
+
| `eslint-plugin-jsx-a11y >= 6` | React config - neighbor extends it, not replaces it |
|
|
179
227
|
| `eslint-plugin-vuejs-accessibility >= 2` | Vue config |
|
|
180
228
|
| `@angular-eslint/eslint-plugin-template >= 17` | Angular config |
|
|
181
229
|
| `stylelint >= 14` | Stylelint config |
|
|
@@ -184,7 +232,7 @@ All peers are optional. Install only what your project uses.
|
|
|
184
232
|
|
|
185
233
|
## What neighbor adds
|
|
186
234
|
|
|
187
|
-
### ESLint
|
|
235
|
+
### ESLint - React / JSX
|
|
188
236
|
|
|
189
237
|
Base: `eslint-plugin-jsx-a11y`
|
|
190
238
|
|
|
@@ -198,8 +246,8 @@ Base: `eslint-plugin-jsx-a11y`
|
|
|
198
246
|
| `role="dialog"` requires accessible name | `no-roles-without-name` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
199
247
|
| `role="group"` with form controls requires name | `no-group-without-name` | [1.3.1](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships) |
|
|
200
248
|
| `role="tooltip"` requires `id` on the tooltip | `no-tooltip-role-misuse` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
201
|
-
| `role="application"` disables AT browse mode | `no-application-role` |
|
|
202
|
-
| `role="grid"` almost always wrong | `no-grid-role` |
|
|
249
|
+
| `role="application"` disables AT browse mode | `no-application-role` | - |
|
|
250
|
+
| `role="grid"` almost always wrong | `no-grid-role` | - |
|
|
203
251
|
| `role="menu"` on nav triggers wrong AT mode | `no-menu-role-on-nav` | [2.1.1](https://www.w3.org/WAI/WCAG21/Understanding/keyboard) |
|
|
204
252
|
| `role="presentation"` on a focusable element | `no-presentation-on-focusable` | [2.1.1](https://www.w3.org/WAI/WCAG21/Understanding/keyboard) |
|
|
205
253
|
| `role="log"` must not contain interactive children | `no-log-with-interactive-children` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
@@ -212,9 +260,9 @@ Base: `eslint-plugin-jsx-a11y`
|
|
|
212
260
|
| `role="listbox"` requires `role="option"` children | `no-listbox-without-option` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
213
261
|
| `role="tree"` requires `role="treeitem"` children | `no-tree-without-treeitem` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
214
262
|
| `role="feed"` requires `role="article"` children | `no-feed-without-article` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
215
|
-
| `aria-hidden="true"` + `role="none"` is redundant | `no-redundant-aria-hidden-with-presentation` |
|
|
216
|
-
| `aria-roledescription` does not translate | `no-aria-roledescription` |
|
|
217
|
-
| `aria-readonly` has poor AT support | `no-aria-readonly` |
|
|
263
|
+
| `aria-hidden="true"` + `role="none"` is redundant | `no-redundant-aria-hidden-with-presentation` | - |
|
|
264
|
+
| `aria-roledescription` does not translate | `no-aria-roledescription` | - |
|
|
265
|
+
| `aria-readonly` has poor AT support | `no-aria-readonly` | - |
|
|
218
266
|
| `aria-owns` on a void element | `no-aria-owns-on-void` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
219
267
|
| `aria-activedescendant` requires a non-empty static ID | `no-aria-activedescendant-without-id` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
220
268
|
| `aria-required` only valid on form-control roles | `no-aria-required-on-non-form` | [4.1.2](https://www.w3.org/WAI/WCAG21/Understanding/name-role-value) |
|
|
@@ -238,7 +286,7 @@ Base: `eslint-plugin-jsx-a11y`
|
|
|
238
286
|
| `<th>` or header role with no accessible text | `no-empty-table-header` | [1.3.1](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships) |
|
|
239
287
|
| `announce()` called in component render body | `no-announce-in-render` | [4.1.3](https://www.w3.org/WAI/WCAG21/Understanding/status-messages) |
|
|
240
288
|
|
|
241
|
-
### ESLint
|
|
289
|
+
### ESLint - Remix 2
|
|
242
290
|
|
|
243
291
|
Same as React / JSX. Additional rules activate when Remix imports are detected in the file being linted:
|
|
244
292
|
|
|
@@ -247,7 +295,7 @@ Same as React / JSX. Additional rules activate when Remix imports are detected i
|
|
|
247
295
|
| `@ulam` hash router alongside `react-router` | `no-hash-router-in-remix` | warn |
|
|
248
296
|
| `usePageTitle()` alongside `react-router` | `no-use-page-title-in-remix` | warn |
|
|
249
297
|
|
|
250
|
-
### ESLint
|
|
298
|
+
### ESLint - Vue SFCs
|
|
251
299
|
|
|
252
300
|
Base: `eslint-plugin-vuejs-accessibility`
|
|
253
301
|
|
|
@@ -266,21 +314,39 @@ Neighbor adds everything in the React table above, adapted for Vue's AST (`v-htm
|
|
|
266
314
|
| `scope` on `<td>` (only valid on `<th>`) | `no-scope-on-td` | [1.3.1](https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships) |
|
|
267
315
|
| `announce()` called outside `onMounted`/`watch`/handler | `no-announce-in-render` | [4.1.3](https://www.w3.org/WAI/WCAG21/Understanding/status-messages) |
|
|
268
316
|
|
|
269
|
-
### ESLint
|
|
317
|
+
### ESLint - Angular templates
|
|
270
318
|
|
|
271
319
|
Base: `@angular-eslint/eslint-plugin-template`
|
|
272
320
|
|
|
273
|
-
Neighbor adds the same rule set as Vue, adapted for Angular's template AST (`[innerHTML]` instead of `dangerouslySetInnerHTML`). The `no-announce-in-render` rule also lints Angular component TypeScript files
|
|
321
|
+
Neighbor adds the same rule set as Vue, adapted for Angular's template AST (`[innerHTML]` instead of `dangerouslySetInnerHTML`). The `no-announce-in-render` rule also lints Angular component TypeScript files - see the setup instructions for how to configure it for `.ts` files alongside `.html` templates.
|
|
274
322
|
|
|
275
323
|
**Known limitation:** Angular's template parser does not attach parent pointers to AST nodes. Rules that need to walk up the tree (`no-summary-without-details`, `no-button-type-missing`, `no-log-with-interactive-children`, `no-menu-role-on-nav`, `no-heading-inside-interactive`) will silently pass in Angular templates. The `no-dynamic-content-without-live` rule only checks the element itself for Angular (no ancestor walk).
|
|
276
324
|
|
|
277
|
-
### Stylelint
|
|
325
|
+
### Stylelint - CSS
|
|
278
326
|
|
|
279
327
|
| Rule | What it checks |
|
|
280
328
|
| --- | --- |
|
|
281
|
-
| `
|
|
282
|
-
| `
|
|
283
|
-
| `
|
|
329
|
+
| `neighbor/user-preferences` | Warns when motion, transparency, or alpha colors are used without `@media (prefers-*)` fallbacks - [SC 1.4.3](https://www.w3.org/WAI/WCAG21/Understanding/contrast-minimum) / [SC 2.3.3](https://www.w3.org/WAI/WCAG21/Understanding/animation-from-interactions) |
|
|
330
|
+
| `neighbor/no-outline-none` | Disallows bare `outline: none` or `outline: 0` outside `:focus` selectors - [SC 2.4.7](https://www.w3.org/WAI/WCAG21/Understanding/focus-visible) |
|
|
331
|
+
| `neighbor/no-forced-colors-none` | Disallows `forced-color-adjust: none` inside `@media (forced-colors)` - opts out of Windows High Contrast Mode - [SC 1.4.11](https://www.w3.org/WAI/WCAG21/Understanding/non-text-contrast) |
|
|
332
|
+
|
|
333
|
+
### Content linter
|
|
334
|
+
|
|
335
|
+
Rules that flag accessibility and inclusion problems in web and app copy. Works on string literals and JSX text in JS/TS/JSX/TSX files.
|
|
336
|
+
|
|
337
|
+
| Rule | What it flags | Severity | WCAG SC |
|
|
338
|
+
| --- | --- | --- | --- |
|
|
339
|
+
| `no-ableist-language` | Slurs, condescending euphemisms, suffering-framing ("suffers from", "wheelchair-bound", "special needs") | warn | [3.1.1](https://www.w3.org/WAI/WCAG22/Understanding/language-of-page) |
|
|
340
|
+
| `no-disability-metaphor` | Figurative use of disability language ("blind spot", "tone deaf", "paralyzed by") | warn | - |
|
|
341
|
+
| `no-english-idiom` | Idioms and sports metaphors opaque to ESL readers ("ball park", "slam dunk", "boil the ocean") | warn | [3.1.5](https://www.w3.org/WAI/WCAG22/Understanding/reading-level) |
|
|
342
|
+
| `no-vague-cta` | Vague link and button text ("click here", "read more", "here") | warn | [2.4.4](https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context) |
|
|
343
|
+
| `no-directional-language` | Layout-dependent position references ("see above", "in the right sidebar") | warn | [1.3.3](https://www.w3.org/WAI/WCAG22/Understanding/sensory-characteristics) |
|
|
344
|
+
| `no-unexplained-abbreviation` | Acronyms used without a prior expansion in the same file | warn | [3.1.4](https://www.w3.org/WAI/WCAG22/Understanding/abbreviations) |
|
|
345
|
+
| `no-all-caps-prose` | ALL CAPS words in prose that screen readers may spell out letter-by-letter | warn | - |
|
|
346
|
+
| `no-vague-error-message` | Error messages that don't explain what went wrong ("An error occurred", "Something went wrong") | warn | [3.3.1](https://www.w3.org/WAI/WCAG22/Understanding/error-identification) |
|
|
347
|
+
| `no-ampersand-in-prose` | `&` used in place of "and" in prose - announced inconsistently by screen readers | warn | - |
|
|
348
|
+
|
|
349
|
+
See [RULES-CONTENT.md](RULES-CONTENT.md) for the full rule reference including sources, methodology, and the language-evolution note.
|
|
284
350
|
|
|
285
351
|
## Rule severity
|
|
286
352
|
|
|
@@ -288,13 +354,17 @@ Neighbor adds the same rule set as Vue, adapted for Angular's template AST (`[in
|
|
|
288
354
|
| --- | --- |
|
|
289
355
|
| `error` | Definite AT breakage or HTML spec violation |
|
|
290
356
|
| `warn` | Strong guidance, occasional legitimate overrides exist |
|
|
291
|
-
| `off` | Available but disabled
|
|
357
|
+
| `off` | Available but disabled - too noisy for most codebases, enable if it fits your project |
|
|
292
358
|
|
|
293
359
|
All rules can be overridden in your config.
|
|
294
360
|
|
|
295
361
|
## See also
|
|
296
362
|
|
|
297
|
-
- [RULES.md](RULES.md)
|
|
363
|
+
- [RULES.md](RULES.md) - rule index across all domains
|
|
364
|
+
- [RULES-MARKUP.md](RULES-MARKUP.md) - full ESLint rule reference (markup)
|
|
365
|
+
- [RULES-CSS.md](RULES-CSS.md) - full Stylelint rule reference (CSS)
|
|
366
|
+
- [RULES-CONTENT.md](RULES-CONTENT.md) - full content rule reference with sources
|
|
367
|
+
- [neighbor-vale](https://github.com/a11yfred/neighbor-vale) - companion Vale package for prose linting in Markdown, MDX, and HTML
|
|
298
368
|
|
|
299
369
|
## License
|
|
300
370
|
|