@diegovelasquezweb/a11y-engine 0.7.9 → 0.8.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/package.json +1 -1
- package/src/ai/claude.mjs +9 -8
- package/src/ai/enrich.mjs +8 -1
- package/src/reports/renderers/html.mjs +11 -11
package/package.json
CHANGED
package/src/ai/claude.mjs
CHANGED
|
@@ -35,17 +35,18 @@ function buildSystemPrompt(context) {
|
|
|
35
35
|
|
|
36
36
|
return `You are an expert web accessibility engineer specializing in WCAG 2.2 AA remediation.
|
|
37
37
|
|
|
38
|
-
Your task is to
|
|
38
|
+
Your task is to provide a developer-friendly AI hint for each accessibility finding — something MORE USEFUL than the generic automated fix already provided.
|
|
39
39
|
${stackInfo ? `\nProject context:\n${stackInfo}` : ""}
|
|
40
|
-
For each finding
|
|
41
|
-
1. A
|
|
42
|
-
2.
|
|
40
|
+
For each finding, provide:
|
|
41
|
+
1. fixDescription: A 2-3 sentence explanation that goes BEYOND the generic fix. Explain WHY this matters for real users, WHAT specifically to look for in the codebase, and HOW to verify the fix works. Be specific to the selector and actual violation data provided.
|
|
42
|
+
2. fixCode: A ready-to-use, production-quality code snippet in the correct syntax for the stack. Do NOT copy the existing fix code — write a BETTER, more complete example that a developer can use directly.
|
|
43
43
|
|
|
44
44
|
Rules:
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
45
|
+
- Your fixDescription must add new insight not present in currentFix — don't paraphrase it
|
|
46
|
+
- Your fixCode must be different and more complete than currentCode
|
|
47
|
+
- Use framework-specific syntax (JSX/TSX for React/Next.js, SFC for Vue, etc.)
|
|
48
|
+
- Reference the actual selector or element from the finding when possible
|
|
49
|
+
- If the violation data contains specific values (colors, ratios, labels), use them in your response
|
|
49
50
|
- Respond in JSON only — no markdown, no explanation outside the JSON structure`;
|
|
50
51
|
}
|
|
51
52
|
|
package/src/ai/enrich.mjs
CHANGED
|
@@ -52,7 +52,14 @@ async function main() {
|
|
|
52
52
|
f.fixDescription !== original.fixDescription ||
|
|
53
53
|
f.fixCode !== original.fixCode
|
|
54
54
|
);
|
|
55
|
-
|
|
55
|
+
if (!wasImproved) return f;
|
|
56
|
+
return {
|
|
57
|
+
...original,
|
|
58
|
+
aiEnhanced: true,
|
|
59
|
+
ai_fix_description: f.fixDescription || null,
|
|
60
|
+
ai_fix_code: f.fixCode || null,
|
|
61
|
+
ai_fix_code_lang: f.fixCodeLang || null,
|
|
62
|
+
};
|
|
56
63
|
});
|
|
57
64
|
|
|
58
65
|
writeJson(findingsPath, { ...payload, findings: enrichedWithFlag });
|
|
@@ -100,7 +100,7 @@ export function buildIssueCard(finding) {
|
|
|
100
100
|
<div class="space-y-2">
|
|
101
101
|
${stackNotes.map(({ key, note, style }) => `
|
|
102
102
|
<div class="flex gap-2 items-start">
|
|
103
|
-
<span class="
|
|
103
|
+
<span class="shrink-0 px-2 py-0.5 rounded text-[10px] font-bold border ${style} uppercase tracking-wider mt-0.5">${escapeHtml(key)}</span>
|
|
104
104
|
<p class="text-[12px] text-slate-600 leading-relaxed">${escapeHtml(note)}</p>
|
|
105
105
|
</div>`).join("")}
|
|
106
106
|
</div>
|
|
@@ -135,7 +135,7 @@ export function buildIssueCard(finding) {
|
|
|
135
135
|
</div>`;
|
|
136
136
|
|
|
137
137
|
const fixPanelHtml = `
|
|
138
|
-
<div class="bg-
|
|
138
|
+
<div class="bg-linear-to-br from-indigo-50 to-white border border-indigo-100/80 rounded-xl p-5 relative overflow-hidden shadow-sm">
|
|
139
139
|
<div class="absolute top-0 right-0 p-4 opacity-[0.03] transform translate-x-4 -translate-y-4 pointer-events-none">
|
|
140
140
|
<svg class="w-32 h-32 text-indigo-900" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" clip-rule="evenodd"></path></svg>
|
|
141
141
|
</div>
|
|
@@ -188,7 +188,7 @@ export function buildIssueCard(finding) {
|
|
|
188
188
|
Visual Evidence
|
|
189
189
|
</h4>
|
|
190
190
|
<div class="bg-slate-50/50 p-2 rounded-xl border border-slate-200/60 inline-block shadow-sm">
|
|
191
|
-
<img src="${escapeHtml(finding.screenshotPath)}" alt="Screenshot of ${escapeHtml(finding.title)}" class="rounded-lg border border-slate-200 shadow-sm max-h-
|
|
191
|
+
<img src="${escapeHtml(finding.screenshotPath)}" alt="Screenshot of ${escapeHtml(finding.title)}" class="rounded-lg border border-slate-200 shadow-sm max-h-90 w-auto object-contain bg-white" loading="lazy">
|
|
192
192
|
</div>
|
|
193
193
|
</div>`
|
|
194
194
|
: "";
|
|
@@ -251,7 +251,7 @@ export function buildIssueCard(finding) {
|
|
|
251
251
|
return `
|
|
252
252
|
<article class="issue-card bg-white/90 backdrop-blur-xl rounded-2xl border ${borderClass} shadow-sm transition-all duration-300 hover:shadow-lg hover:-translate-y-0.5 mb-8 overflow-hidden group" data-severity="${finding.severity}" data-rule-id="${escapeHtml(finding.ruleId)}" data-wcag="${escapeHtml(finding.wcag)}" data-collapsed="true" id="${escapeHtml(finding.id)}">
|
|
253
253
|
<button
|
|
254
|
-
class="card-header w-full text-left p-5 md:p-6 bg-
|
|
254
|
+
class="card-header w-full text-left p-5 md:p-6 bg-linear-to-r from-white to-slate-50/80 cursor-pointer select-none relative focus:outline-none focus:ring-4 focus:ring-indigo-500/20"
|
|
255
255
|
onclick="toggleCard(this)"
|
|
256
256
|
aria-expanded="false"
|
|
257
257
|
aria-controls="body-${escapeHtml(finding.id)}"
|
|
@@ -270,15 +270,15 @@ export function buildIssueCard(finding) {
|
|
|
270
270
|
<div class="flex flex-wrap gap-x-4 gap-y-2 text-[13px] text-slate-600 font-medium">
|
|
271
271
|
<div class="flex items-center gap-1.5 bg-slate-50/50 px-2 py-1 rounded-md border border-slate-100">
|
|
272
272
|
<svg class="w-3.5 h-3.5 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"></path></svg>
|
|
273
|
-
<span class="truncate max-w-
|
|
273
|
+
<span class="truncate max-w-50 md:max-w-md transition-colors searchable-field issue-url">${escapeHtml(finding.url)}</span>
|
|
274
274
|
</div>
|
|
275
275
|
<div class="flex items-center gap-1.5 min-w-0 bg-slate-50/50 px-2 py-1 rounded-md border border-slate-100">
|
|
276
|
-
<svg class="w-3.5 h-3.5 text-slate-500
|
|
276
|
+
<svg class="w-3.5 h-3.5 text-slate-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path></svg>
|
|
277
277
|
<code class="text-[12px] text-slate-800 font-mono truncate min-w-0 flex-1 searchable-field issue-selector">${escapeHtml(finding.selector)}</code>
|
|
278
278
|
</div>
|
|
279
279
|
</div>
|
|
280
280
|
</div>
|
|
281
|
-
<div class="bg-white p-1.5 rounded-full border border-slate-200 shadow-sm group-hover:bg-slate-50 transition-colors mt-1
|
|
281
|
+
<div class="bg-white p-1.5 rounded-full border border-slate-200 shadow-sm group-hover:bg-slate-50 transition-colors mt-1 shrink-0">
|
|
282
282
|
<svg class="card-chevron w-5 h-5 text-slate-500 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg>
|
|
283
283
|
</div>
|
|
284
284
|
</div>
|
|
@@ -323,7 +323,7 @@ export function buildManualCheckCard(check) {
|
|
|
323
323
|
|
|
324
324
|
const conditionalNote = check.conditional
|
|
325
325
|
? `<div class="mb-5 flex items-start gap-2.5 bg-amber-50 border border-amber-200 rounded-xl px-4 py-3">
|
|
326
|
-
<svg class="w-4 h-4 text-amber-500 mt-0.5
|
|
326
|
+
<svg class="w-4 h-4 text-amber-500 mt-0.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
|
327
327
|
<p class="text-[12px] text-amber-800 font-medium leading-relaxed">${escapeHtml(check.conditional)}</p>
|
|
328
328
|
</div>`
|
|
329
329
|
: "";
|
|
@@ -346,7 +346,7 @@ export function buildManualCheckCard(check) {
|
|
|
346
346
|
|
|
347
347
|
return `
|
|
348
348
|
<article class="manual-card bg-white/90 backdrop-blur-xl rounded-2xl border border-amber-200 hover:border-amber-300 shadow-sm transition-all duration-300 hover:shadow-lg hover:-translate-y-0.5 mb-4 overflow-hidden group" id="${id}" data-criterion="${escapeHtml(check.criterion)}" data-level="${escapeHtml(check.level)}" data-state="" data-collapsed="true">
|
|
349
|
-
<div class="manual-header flex items-stretch bg-
|
|
349
|
+
<div class="manual-header flex items-stretch bg-linear-to-r from-amber-50/60 to-white">
|
|
350
350
|
<button
|
|
351
351
|
class="card-header flex-1 text-left p-5 md:p-6 cursor-pointer select-none relative focus:outline-none focus:ring-4 focus:ring-amber-500/20"
|
|
352
352
|
onclick="toggleCard(this)"
|
|
@@ -362,12 +362,12 @@ export function buildManualCheckCard(check) {
|
|
|
362
362
|
</div>
|
|
363
363
|
<h3 class="text-base font-extrabold text-slate-900 group-hover:text-amber-900 transition-colors">${escapeHtml(check.title)}</h3>
|
|
364
364
|
</div>
|
|
365
|
-
<div class="bg-white p-1.5 rounded-full border border-amber-200 shadow-sm group-hover:bg-amber-50 transition-colors
|
|
365
|
+
<div class="bg-white p-1.5 rounded-full border border-amber-200 shadow-sm group-hover:bg-amber-50 transition-colors shrink-0">
|
|
366
366
|
<svg class="card-chevron w-5 h-5 text-amber-500 transition-transform duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path></svg>
|
|
367
367
|
</div>
|
|
368
368
|
</div>
|
|
369
369
|
</button>
|
|
370
|
-
<div class="flex items-center gap-2 px-4 border-l border-amber-100
|
|
370
|
+
<div class="flex items-center gap-2 px-4 border-l border-amber-100 shrink-0">
|
|
371
371
|
<button class="manual-verified-btn px-3 py-1.5 rounded-full text-xs font-bold border border-slate-200 bg-white text-slate-500 hover:border-emerald-300 hover:text-emerald-700 hover:bg-emerald-50 transition-colors whitespace-nowrap" onclick="setManualState('${escapeHtml(check.criterion)}', 'verified')">✓ Verified</button>
|
|
372
372
|
<button class="manual-na-btn px-3 py-1.5 rounded-full text-xs font-bold border border-slate-200 bg-white text-slate-500 hover:border-slate-400 hover:text-slate-600 hover:bg-slate-50 transition-colors whitespace-nowrap" onclick="setManualState('${escapeHtml(check.criterion)}', 'na')">N/A</button>
|
|
373
373
|
</div>
|