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.
@@ -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
+ }