wcag-a11y 0.1.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 +44 -0
- package/dist/ai/gemini.js +57 -0
- package/dist/ai/index.js +12 -0
- package/dist/ai/ollama.js +55 -0
- package/dist/ai/prompt.js +20 -0
- package/dist/ai/types.js +1 -0
- package/dist/cli.js +67 -0
- package/dist/config.js +32 -0
- package/dist/crawler.js +54 -0
- package/dist/demo.js +89 -0
- package/dist/engine/index.js +56 -0
- package/dist/engine/rules/aria.js +293 -0
- package/dist/engine/rules/color-contrast.js +114 -0
- package/dist/engine/rules/forms.js +255 -0
- package/dist/engine/rules/keyboard.js +229 -0
- package/dist/engine/rules/language.js +31 -0
- package/dist/engine/rules/links.js +166 -0
- package/dist/engine/rules/media.js +104 -0
- package/dist/engine/rules/structure.js +301 -0
- package/dist/engine/rules/tables.js +159 -0
- package/dist/engine/rules/text-alternatives.js +202 -0
- package/dist/engine/types.js +1 -0
- package/dist/reporter/markdown.js +42 -0
- package/dist/reporter/terminal.js +55 -0
- package/package.json +33 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writeFileSync } from 'fs';
|
|
2
|
+
const IMPACT_EMOJI = {
|
|
3
|
+
critical: '๐ด',
|
|
4
|
+
serious: '๐ ',
|
|
5
|
+
moderate: '๐ก',
|
|
6
|
+
minor: '๐ข',
|
|
7
|
+
};
|
|
8
|
+
export function generateMarkdownReport(result, fixes, outputPath = 'a11y-report.md') {
|
|
9
|
+
const lines = [
|
|
10
|
+
'# WCAG A11y Report',
|
|
11
|
+
`> Generated: ${new Date().toLocaleString()}`,
|
|
12
|
+
'',
|
|
13
|
+
'## Summary',
|
|
14
|
+
'',
|
|
15
|
+
`| Impact | Count |`,
|
|
16
|
+
`|--------|-------|`,
|
|
17
|
+
`| ๐ด Critical | ${result.criticalCount} |`,
|
|
18
|
+
`| ๐ Serious | ${result.seriousCount} |`,
|
|
19
|
+
`| ๐ก Moderate | ${result.moderateCount} |`,
|
|
20
|
+
`| ๐ข Minor | ${result.minorCount} |`,
|
|
21
|
+
'',
|
|
22
|
+
'---',
|
|
23
|
+
'',
|
|
24
|
+
];
|
|
25
|
+
for (const page of result.pages) {
|
|
26
|
+
lines.push(`## Page: ${page.url}`, '');
|
|
27
|
+
if (page.violations.length === 0) {
|
|
28
|
+
lines.push('โ
No violations found.', '', '---', '');
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
for (const v of page.violations) {
|
|
32
|
+
const fix = fixes.find((f) => f.ruleId === v.ruleId && v.selector.includes(f.selector?.split(' ')[0] ?? ''));
|
|
33
|
+
lines.push(`### ${IMPACT_EMOJI[v.impact] ?? 'โช'} [${v.impact.toUpperCase()}] ${v.description}`, '', `**Rule:** \`${v.ruleId}\` `, `**WCAG:** ${fix?.wcagReference ?? `SC ${v.wcag} (Level ${v.level})`} `, `**Selector:** \`${v.selector}\``, '', '**Violating element:**', '```html', v.html, '```', '');
|
|
34
|
+
if (fix) {
|
|
35
|
+
lines.push('**Why it matters:**', fix.explanation, '', '**Fixed code:**', '```html', fix.fixedCode, '```', '', '**๐ Prompt for your AI assistant (Cursor / Copilot / Claude):**', '```', fix.optimalPrompt, '```', '');
|
|
36
|
+
}
|
|
37
|
+
lines.push('---', '');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
writeFileSync(outputPath, lines.join('\n'), 'utf-8');
|
|
41
|
+
console.log(`\nReport saved โ ${outputPath}`);
|
|
42
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
const IMPACT_COLOR = {
|
|
3
|
+
critical: chalk.red,
|
|
4
|
+
serious: chalk.yellow,
|
|
5
|
+
moderate: chalk.blue,
|
|
6
|
+
minor: chalk.gray,
|
|
7
|
+
};
|
|
8
|
+
export function printTerminalReport(result) {
|
|
9
|
+
console.log('\n' + chalk.bold('WCAG A11y') + ' โ scan complete\n' + chalk.gray('โ'.repeat(60)));
|
|
10
|
+
for (const page of result.pages) {
|
|
11
|
+
const url = chalk.cyan(page.url);
|
|
12
|
+
if (page.violations.length === 0) {
|
|
13
|
+
console.log(`\n ${chalk.green('โ')} ${url} ${chalk.green('No violations found')}`);
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const c = page.violations.filter((v) => v.impact === 'critical').length;
|
|
17
|
+
const s = page.violations.filter((v) => v.impact === 'serious').length;
|
|
18
|
+
const m = page.violations.filter((v) => v.impact === 'moderate').length;
|
|
19
|
+
const counts = [
|
|
20
|
+
c > 0 ? chalk.red(`${c} critical`) : '',
|
|
21
|
+
s > 0 ? chalk.yellow(`${s} serious`) : '',
|
|
22
|
+
m > 0 ? chalk.blue(`${m} moderate`) : '',
|
|
23
|
+
].filter(Boolean).join(' ');
|
|
24
|
+
console.log(`\n ${chalk.red('โ')} ${url} ${counts}`);
|
|
25
|
+
for (const v of page.violations) {
|
|
26
|
+
const color = IMPACT_COLOR[v.impact] ?? chalk.white;
|
|
27
|
+
const tag = color(`[${v.impact.toUpperCase()}]`);
|
|
28
|
+
const wcag = chalk.gray(`WCAG ${v.wcag}`);
|
|
29
|
+
console.log(` ${tag} ${v.description} ${wcag}`);
|
|
30
|
+
console.log(` ${chalk.gray('โ')} ${chalk.dim(v.selector)}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
console.log('\n' + chalk.gray('โ'.repeat(60)));
|
|
34
|
+
console.log(`Total: ${chalk.red(result.criticalCount + ' critical')} ยท ${chalk.yellow(result.seriousCount + ' serious')} ยท ${chalk.blue(result.moderateCount + ' moderate')}`);
|
|
35
|
+
console.log(chalk.gray('Run with --report to save a full markdown report with AI fix suggestions.\n'));
|
|
36
|
+
}
|
|
37
|
+
export function printAIPrompts(fixes) {
|
|
38
|
+
if (fixes.length === 0)
|
|
39
|
+
return;
|
|
40
|
+
console.log('\n' + chalk.bold.magenta('AI Fix Prompts') + chalk.gray(' โ paste any of these into Cursor, Copilot, or Claude'));
|
|
41
|
+
console.log(chalk.gray('โ'.repeat(60)));
|
|
42
|
+
for (const fix of fixes) {
|
|
43
|
+
const header = chalk.bold(`[${fix.ruleId}]`) + (fix.selector ? chalk.gray(` ${fix.selector}`) : '');
|
|
44
|
+
console.log('\n' + header);
|
|
45
|
+
if (fix.explanation) {
|
|
46
|
+
console.log(chalk.gray(fix.explanation));
|
|
47
|
+
}
|
|
48
|
+
console.log(chalk.cyan('โโ Copy this prompt โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
49
|
+
for (const line of fix.optimalPrompt.split('\n')) {
|
|
50
|
+
console.log(chalk.cyan('โ ') + line);
|
|
51
|
+
}
|
|
52
|
+
console.log(chalk.cyan('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
53
|
+
}
|
|
54
|
+
console.log('');
|
|
55
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wcag-a11y",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "WCAG 2.1/2.2 accessibility auditor with AI-powered fixes",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"wcag-a11y": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=20.0.0"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist/",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsx src/cli.ts",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"commander": "^14.0.0",
|
|
24
|
+
"playwright": "^1.60.0",
|
|
25
|
+
"chalk": "^5.6.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"typescript": "^6.0.0",
|
|
29
|
+
"@types/node": "^25.9.0",
|
|
30
|
+
"vitest": "^4.1.0",
|
|
31
|
+
"tsx": "^4.22.0"
|
|
32
|
+
}
|
|
33
|
+
}
|