eslint-plugin-tailwind-a11y 0.1.0 → 0.2.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/README.md +15 -53
- package/dist/rules/contrast.js +18 -8
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
# eslint-plugin-tailwind-a11y
|
|
2
2
|
|
|
3
|
-
ESLint rules
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
you have to remember to invoke.
|
|
7
|
-
|
|
8
|
-
This plugin is a thin ESLint adapter over the [`tailwind-a11y`](https://github.com/chamroro/tailwind-a11y/tree/main/packages/tailwind-a11y)
|
|
9
|
-
engine, which does the actual work: resolving Tailwind utility classes back into real
|
|
10
|
-
computed values (colors, sizes) via AST analysis. Every rule here calls straight into
|
|
11
|
-
that engine and reports its violations — there's no separate implementation to drift out
|
|
12
|
-
of sync, and no wording that differs between `npx tailwind-a11y` and `eslint .`.
|
|
3
|
+
ESLint rules for [`tailwind-a11y`](https://github.com/chamroro/tailwind-a11y/tree/main/packages/tailwind-a11y) —
|
|
4
|
+
inline WCAG diagnostics during normal linting, powered by the same engine as the CLI
|
|
5
|
+
and VS Code extension. No detection logic is duplicated, so results never disagree.
|
|
13
6
|
|
|
14
7
|
## Install
|
|
15
8
|
|
|
@@ -25,17 +18,12 @@ Requires ESLint 9 or 10 (flat config).
|
|
|
25
18
|
// eslint.config.js
|
|
26
19
|
import tailwindA11y from "eslint-plugin-tailwind-a11y";
|
|
27
20
|
|
|
28
|
-
export default [
|
|
29
|
-
...tailwindA11y.configs.recommended,
|
|
30
|
-
];
|
|
21
|
+
export default [...tailwindA11y.configs.recommended];
|
|
31
22
|
```
|
|
32
23
|
|
|
33
|
-
|
|
34
|
-
as errors. To register rules manually instead:
|
|
24
|
+
Or register rules individually:
|
|
35
25
|
|
|
36
26
|
```js
|
|
37
|
-
import tailwindA11y from "eslint-plugin-tailwind-a11y";
|
|
38
|
-
|
|
39
27
|
export default [
|
|
40
28
|
{
|
|
41
29
|
files: ["**/*.jsx", "**/*.tsx"],
|
|
@@ -51,46 +39,20 @@ export default [
|
|
|
51
39
|
|
|
52
40
|
## Rules
|
|
53
41
|
|
|
54
|
-
| Rule | WCAG |
|
|
42
|
+
| Rule | WCAG | Detects |
|
|
55
43
|
|---|---|---|
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
|
|
60
|
-
Each rule's exact scope and known limitations are documented in the
|
|
61
|
-
[engine's README](https://github.com/chamroro/tailwind-a11y/tree/main/packages/tailwind-a11y#what-it-deliberately-doesnt-catch-v1-scope) —
|
|
62
|
-
this plugin doesn't change what's checked, only how it's surfaced.
|
|
63
|
-
|
|
64
|
-
## `.tsx` files and parsers
|
|
44
|
+
| `contrast` | 1.4.3 (AA) | Low-contrast `text-*`/`bg-*` pairs — suggests the nearest passing shade |
|
|
45
|
+
| `touch-target` | 2.5.8 (AA) | Interactive elements under 24×24px |
|
|
46
|
+
| `focus-indicator` | 2.4.7 (AA) | `focus:outline-none` with no visible replacement |
|
|
65
47
|
|
|
66
|
-
|
|
67
|
-
`typescript` plugins), so `.tsx` syntax is understood regardless of what parser your
|
|
68
|
-
ESLint config uses. But ESLint itself still needs to parse the file *first* to run any
|
|
69
|
-
rule at all — if your config doesn't already set a TypeScript-aware
|
|
70
|
-
`languageOptions.parser` (e.g. `typescript-eslint`) for `.tsx` files, ESLint will fail to
|
|
71
|
-
parse them before this plugin ever runs. If you already lint `.tsx` files today, you have
|
|
72
|
-
this covered; nothing extra to add for this plugin specifically.
|
|
48
|
+
Exact scope and known limitations: [engine README](https://github.com/chamroro/tailwind-a11y/tree/main/packages/tailwind-a11y#scope).
|
|
73
49
|
|
|
74
|
-
##
|
|
75
|
-
|
|
76
|
-
Same checks, same engine, two ways to run them: `eslint-plugin-tailwind-a11y` for
|
|
77
|
-
inline, per-file feedback during normal linting; [`tailwind-a11y`](https://github.com/chamroro/tailwind-a11y/tree/main/packages/tailwind-a11y)
|
|
78
|
-
(the CLI) for a one-shot scan, e.g. in a CI step that doesn't otherwise run ESLint. Use
|
|
79
|
-
either, or both — they'll never disagree, since the plugin doesn't reimplement anything.
|
|
80
|
-
|
|
81
|
-
## Development
|
|
82
|
-
|
|
83
|
-
This package lives in the [`tailwind-a11y` monorepo](https://github.com/chamroro/tailwind-a11y)
|
|
84
|
-
(npm workspaces) alongside the engine it depends on:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
npm install # from the monorepo root
|
|
88
|
-
npm run build --workspaces # or: npm run build -w tailwind-a11y -w eslint-plugin-tailwind-a11y
|
|
89
|
-
npm test --workspaces --if-present
|
|
90
|
-
```
|
|
50
|
+
## Notes
|
|
91
51
|
|
|
92
|
-
|
|
93
|
-
|
|
52
|
+
`.tsx` files need a TypeScript-aware `languageOptions.parser` (e.g. `typescript-eslint`)
|
|
53
|
+
in your own config for ESLint to parse them at all. This plugin's own analysis
|
|
54
|
+
understands TypeScript syntax regardless — but ESLint has to parse the file successfully
|
|
55
|
+
before any rule, including this one, ever runs.
|
|
94
56
|
|
|
95
57
|
## License
|
|
96
58
|
|
package/dist/rules/contrast.js
CHANGED
|
@@ -10,6 +10,11 @@ const rule = {
|
|
|
10
10
|
schema: [],
|
|
11
11
|
messages: {
|
|
12
12
|
contrast: "{{textClass}} on {{bgClass}} — ratio {{ratio}}, needs {{required}} ({{level}})",
|
|
13
|
+
// Separate messageId rather than an optional {{suggestion}} placeholder
|
|
14
|
+
// in the base template — ESLint's interpolate() renders literal
|
|
15
|
+
// "undefined" if the data key is present-but-undefined, and RuleTester
|
|
16
|
+
// hard-fails on an unsubstituted {{placeholder}} if the key is omitted.
|
|
17
|
+
contrastWithSuggestion: "{{textClass}} on {{bgClass}} — ratio {{ratio}}, needs {{required}} ({{level}}); try {{suggestion}} ({{suggestedRatio}})",
|
|
13
18
|
},
|
|
14
19
|
},
|
|
15
20
|
create(context) {
|
|
@@ -20,16 +25,21 @@ const rule = {
|
|
|
20
25
|
Program() {
|
|
21
26
|
const violations = checkContrast(extractChecks(context.sourceCode.getText(), context.filename));
|
|
22
27
|
for (const v of violations) {
|
|
28
|
+
const data = {
|
|
29
|
+
textClass: v.textClass,
|
|
30
|
+
bgClass: v.bgClass,
|
|
31
|
+
ratio: v.ratio.toFixed(2),
|
|
32
|
+
required: v.required,
|
|
33
|
+
level: v.level,
|
|
34
|
+
};
|
|
35
|
+
if (v.suggestion) {
|
|
36
|
+
data.suggestion = v.suggestion;
|
|
37
|
+
data.suggestedRatio = v.suggestedRatio.toFixed(2);
|
|
38
|
+
}
|
|
23
39
|
context.report({
|
|
24
40
|
loc: { line: v.line, column: 0 },
|
|
25
|
-
messageId: "contrast",
|
|
26
|
-
data
|
|
27
|
-
textClass: v.textClass,
|
|
28
|
-
bgClass: v.bgClass,
|
|
29
|
-
ratio: v.ratio.toFixed(2),
|
|
30
|
-
required: v.required,
|
|
31
|
-
level: v.level,
|
|
32
|
-
},
|
|
41
|
+
messageId: v.suggestion ? "contrastWithSuggestion" : "contrast",
|
|
42
|
+
data,
|
|
33
43
|
});
|
|
34
44
|
}
|
|
35
45
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-tailwind-a11y",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "ESLint rules that catch WCAG accessibility violations — color contrast, touch target size, and focus indicator removal — in Tailwind CSS class combinations.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"./package.json": "./package.json"
|
|
14
14
|
},
|
|
15
|
-
"files": [
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
16
18
|
"engines": {
|
|
17
19
|
"node": "^20.19.0 || ^22.13.0 || >=24"
|
|
18
20
|
},
|
|
@@ -47,7 +49,7 @@
|
|
|
47
49
|
},
|
|
48
50
|
"homepage": "https://github.com/chamroro/tailwind-a11y/tree/main/packages/eslint-plugin-tailwind-a11y#readme",
|
|
49
51
|
"dependencies": {
|
|
50
|
-
"tailwind-a11y": "^0.
|
|
52
|
+
"tailwind-a11y": "^0.2.0"
|
|
51
53
|
},
|
|
52
54
|
"peerDependencies": {
|
|
53
55
|
"eslint": "^9.0.0 || ^10.0.0"
|