Skip to content

Commit f5274f2

Browse files
younglimclaude
andauthored
feat: augment axe violation messages with live DOM context for LLM-assisted fixes (#720)
* feat: augment axe violation messages with actionable context for LLMs Enriches Details messages for color-contrast, color-contrast-enhanced, target-size, valid-lang, and oobee-grading-text-contents with additional context gathered at scan time via Playwright page.evaluate(), so LLMs receive precise, actionable data rather than raw axe output alone. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Bump package version --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 0dab555 commit f5274f2

16 files changed

Lines changed: 1208 additions & 53 deletions

DETAILS_OUTPUT_EXAMPLES.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# Enriched Details Output Examples
2+
3+
These are real outputs captured from `scanPage` against intentionally non-compliant test pages.
4+
The **Details** panel in the HTML report renders the `message` field shown below.
5+
6+
---
7+
8+
## 1. `color-contrast` (WCAG AA — mustFix)
9+
10+
**Element:**
11+
```html
12+
<p style="color: #999999; font-size: 14px;">This light gray text on white background fails AA contrast</p>
13+
```
14+
15+
**Details message:**
16+
```
17+
Multiple text elements in this component fail WCAG 1.4.3 Color Contrast Minimum.
18+
Audit all visible text in the snippet and update every failing foreground color so
19+
normal text achieves at least 4.5:1 contrast against its actual background, with a
20+
safety margin above the minimum where possible. Known failing combinations in this
21+
snippet include foreground #999999 on #ffffff at 14px normal text (current contrast
22+
2.84, expected 4.5:1). Fix all failing text colors in the component, not just the
23+
first reported element. Recommendation: To meet the required contrast ratio, for
24+
foreground #999999 on background #ffffff (target 4.5:1), adjust foreground to #737373
25+
(rgb(115, 115, 115)) or background to #2e2e2e (rgb(46, 46, 46)).
26+
```
27+
28+
**Element:**
29+
```html
30+
<button style="background-color: #55aa99; color: #e8ffe8; font-size: 12px;">Low contrast button</button>
31+
```
32+
33+
**Details message:**
34+
```
35+
Multiple text elements in this component fail WCAG 1.4.3 Color Contrast Minimum.
36+
Audit all visible text in the snippet and update every failing foreground color so
37+
normal text achieves at least 4.5:1 contrast against its actual background, with a
38+
safety margin above the minimum where possible. Known failing combinations in this
39+
snippet include foreground #e8ffe8 on #55aa99 at 12px normal text (current contrast
40+
2.61, expected 4.5:1). Fix all failing text colors in the component, not just the
41+
first reported element. Recommendation: To meet the required contrast ratio, for
42+
foreground #e8ffe8 on background #55aa99 (target 4.5:1), adjust foreground to #003a00
43+
(rgb(0, 58, 0)) or background to #3d7a6e (rgb(61, 122, 110)).
44+
```
45+
46+
---
47+
48+
## 2. `color-contrast-enhanced` (WCAG AAA — goodToFix)
49+
50+
**Element:**
51+
```html
52+
<p style="color: #757575; font-size: 14px;">This text passes AA but fails AAA needs 7 to 1</p>
53+
```
54+
55+
**Details message:**
56+
```
57+
Multiple text elements in this component fail WCAG 1.4.3 Color Contrast Minimum.
58+
Audit all visible text in the snippet and update every failing foreground color so
59+
normal text achieves at least 7:1 contrast against its actual background, with a
60+
safety margin above the minimum where possible. Known failing combinations in this
61+
snippet include foreground #757575 on #ffffff at 14px normal text (current contrast
62+
4.6, expected 7:1). Fix all failing text colors in the component, not just the first
63+
reported element. Recommendation: To meet the required contrast ratio, for foreground
64+
#757575 on background #ffffff (target 7:1), adjust foreground to #555555
65+
(rgb(85, 85, 85)).
66+
```
67+
68+
**Element:**
69+
```html
70+
<p style="color: #6b6b6b; font-size: 12px;">Small text needs 7 to 1 for AAA</p>
71+
```
72+
73+
**Details message:**
74+
```
75+
Multiple text elements in this component fail WCAG 1.4.3 Color Contrast Minimum.
76+
Audit all visible text in the snippet and update every failing foreground color so
77+
normal text achieves at least 7:1 contrast against its actual background, with a
78+
safety margin above the minimum where possible. Known failing combinations in this
79+
snippet include foreground #6b6b6b on #ffffff at 12px normal text (current contrast
80+
5.32, expected 7:1). Fix all failing text colors in the component, not just the first
81+
reported element. Recommendation: To meet the required contrast ratio, for foreground
82+
#6b6b6b on background #ffffff (target 7:1), adjust foreground to #555555
83+
(rgb(85, 85, 85)).
84+
```
85+
86+
---
87+
88+
## 3. `target-size` (WCAG 2.5.8 — mustFix)
89+
90+
### Example A: `content-box` elements (no WARNING)
91+
92+
**Element:**
93+
```html
94+
<a href="/a" class="icon-link" style="width: 16px; height: 16px;">A</a>
95+
```
96+
97+
**Details message:**
98+
```
99+
Fix any of the following:
100+
Target has insufficient size (16px by 16px, should be at least 24px by 24px)
101+
Target has insufficient space to its closest neighbors. Safe clickable space has a
102+
diameter of 17px instead of at least 24px.
103+
Computed hit area: 16px × 16px (box-sizing: content-box).
104+
```
105+
106+
### Example B: `border-box` element with explicit inline width/height (WARNING appended)
107+
108+
**Element:**
109+
```html
110+
<button style="width: 20px; height: 20px; padding: 0; box-sizing: border-box;">X</button>
111+
```
112+
113+
**Details message:**
114+
```
115+
Fix any of the following:
116+
Target has insufficient size (20px by 20px, should be at least 24px by 24px)
117+
Target has insufficient space to its closest neighbors. Safe clickable space has a
118+
diameter of 21px instead of at least 24px.
119+
Computed hit area: 20px × 20px (box-sizing: border-box).
120+
Tip: inline style sets width: 20px, height: 20px with box-sizing: border-box —
121+
padding is included within those dimensions and will not increase the hit area. Fix:
122+
remove the explicit width/height and use min-width: 24px; min-height: 24px instead,
123+
or place the visual content in a child <span> element.
124+
```
125+
126+
---
127+
128+
## 4. `valid-lang` (mustFix)
129+
130+
**Element:**
131+
```html
132+
<div lang="x-klingon">This section also has an invalid private-use lang tag with some sample text content for context.</div>
133+
```
134+
135+
**Details message:**
136+
```
137+
Fix all of the following:
138+
Value of lang attribute not included in the list of valid languages
139+
Note: "x-klingon" uses a private-use "x-" prefix. axe-core's valid-lang rule also
140+
rejects private-use subtags — you must use a registered IANA language code.
141+
Original text: "This section also has an invalid private-use lang tag with some sample
142+
text content for context.". Identify the actual language of this text and use its
143+
registered BCP 47 code (e.g., lang="it" Italian, "es" Spanish, "fr" French,
144+
"de" German, "zh" Chinese, "ja" Japanese, "ko" Korean, "pt" Portuguese, "ar" Arabic).
145+
```
146+
147+
---
148+
149+
## 5. `oobee-grading-text-contents` (WCAG AAA — needsReview)
150+
151+
**Element:**
152+
```html
153+
<html lang="en">...</html>
154+
```
155+
156+
**Details message:**
157+
```
158+
The text content is potentially difficult to read, with a Flesch-Kincaid Reading Ease
159+
score of 33.92. Difficult — college level or above.
160+
```
161+
162+
### Score interpretation table (appended inline after the numeric score)
163+
164+
Only scores in the range 1–50 trigger a violation (scores > 50 pass, scores ≤ 0 are filtered out).
165+
166+
| Score Range | Interpretation appended to message |
167+
|---|---|
168+
| 31–50 | Difficult — college level or above. |
169+
| 1–30 | Very difficult — best understood by university graduates. |
170+
171+
---
172+
173+
## Notes
174+
175+
- **color-contrast** and **color-contrast-enhanced** messages include computed color recommendations using WCAG relative luminance math with a binary search on HSL lightness.
176+
- **target-size** appends `Computed hit area` and, when `box-sizing: border-box` with explicit inline dimensions is detected, a `Tip` explaining why padding won't help.
177+
- **valid-lang** appends a `NOTE` when the lang value uses a private-use `x-*` prefix, plus the element's text content (up to 120 chars) to help identify the correct language code.
178+
- **oobee-grading-text-contents** now appends a plain-language interpretation of the Flesch-Kincaid score immediately after the numeric value.
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/**
2+
* Details Output Demo
3+
*
4+
* Runs scanPage against intentionally non-compliant test pages to capture the
5+
* enriched Details messages for: color-contrast, color-contrast-enhanced,
6+
* target-size, valid-lang, and oobee-grading-text-contents.
7+
*
8+
* Usage: node examples/details-runner.js
9+
*/
10+
import { chromium } from 'playwright';
11+
import { scanPage } from '../dist/npmIndex.js';
12+
import { gradeReadability } from '../dist/crawlers/custom/gradeReadability.js';
13+
14+
// --- Test HTML pages ---
15+
16+
const colorContrastHTML = `
17+
<!DOCTYPE html>
18+
<html lang="en">
19+
<head><title>Color Contrast Test</title></head>
20+
<body style="background-color: #ffffff;">
21+
<h1>Color Contrast Violations</h1>
22+
<p style="color: #999999; font-size: 14px;">This light gray text on white background fails AA contrast</p>
23+
<p style="color: #aaaaaa; font-size: 14px; background-color: #f0f0f0;">Very light gray on light gray</p>
24+
<button style="background-color: #55aa99; color: #e8ffe8; font-size: 12px;">Low contrast button</button>
25+
</body>
26+
</html>
27+
`;
28+
29+
const colorContrastEnhancedHTML = `
30+
<!DOCTYPE html>
31+
<html lang="en">
32+
<head><title>Color Contrast Enhanced Test</title></head>
33+
<body style="background-color: #ffffff;">
34+
<h1>Color Contrast Enhanced AAA Violations</h1>
35+
<p style="color: #757575; font-size: 14px;">This text passes AA but fails AAA needs 7 to 1</p>
36+
<p style="color: #6b6b6b; font-size: 12px;">Small text needs 7 to 1 for AAA</p>
37+
</body>
38+
</html>
39+
`;
40+
41+
const targetSizeHTML = `
42+
<!DOCTYPE html>
43+
<html lang="en">
44+
<head><title>Target Size Test</title>
45+
<style>
46+
body { font-family: sans-serif; padding: 40px; }
47+
.icon-link {
48+
display: inline-block;
49+
width: 16px;
50+
height: 16px;
51+
font-size: 10px;
52+
line-height: 16px;
53+
text-align: center;
54+
text-decoration: none;
55+
color: #333;
56+
overflow: hidden;
57+
}
58+
</style>
59+
</head>
60+
<body>
61+
<main>
62+
<h1>Icon-sized interactive targets</h1>
63+
<a href="/a" class="icon-link" style="width: 16px; height: 16px;">A</a>
64+
<a href="/b" class="icon-link" style="width: 16px; height: 16px;">B</a>
65+
<a href="/c" class="icon-link" style="width: 16px; height: 16px;">C</a>
66+
</main>
67+
</body>
68+
</html>
69+
`;
70+
71+
const validLangHTML = `
72+
<!DOCTYPE html>
73+
<html lang="x-sindarin">
74+
<head><title>Valid Lang Test</title></head>
75+
<body>
76+
<main>
77+
<h1>Valid Lang Violation</h1>
78+
<p>This page uses a private-use language subtag that is not valid according to BCP 47.</p>
79+
<div lang="x-klingon">This section also has an invalid private-use lang tag with some sample text content for context.</div>
80+
</main>
81+
</body>
82+
</html>
83+
`;
84+
85+
const readabilityHTML = `
86+
<!DOCTYPE html>
87+
<html lang="en">
88+
<head><title>Readability Test</title></head>
89+
<body>
90+
<main>
91+
<h1>Building Safety Standards</h1>
92+
<p>The committee reviewed the proposed changes to the building safety standards last Thursday. Members noted that the current regulations do not address modern construction materials adequately. Several technical amendments were suggested to improve clarity for contractors and inspectors. The revised standards will require additional testing for fire resistance in commercial properties. Public consultation on these proposed changes will remain open until the end of next quarter. Building owners should review the draft guidelines to understand potential compliance requirements.</p>
93+
</main>
94+
</body>
95+
</html>
96+
`;
97+
98+
// --- Helpers ---
99+
100+
function extractMessages(result, ruleId) {
101+
const messages = [];
102+
for (const category of ['mustFix', 'goodToFix', 'needsReview']) {
103+
const rules = result?.[category]?.rules;
104+
if (rules && rules[ruleId]) {
105+
const rule = rules[ruleId];
106+
messages.push({
107+
category,
108+
rule: rule.rule || ruleId,
109+
description: rule.description,
110+
totalItems: rule.totalItems,
111+
items: rule.items?.map(item => ({
112+
html: item.html || item.element,
113+
message: item.message,
114+
})),
115+
});
116+
}
117+
}
118+
return messages;
119+
}
120+
121+
// --- Main ---
122+
123+
(async () => {
124+
console.log("Launching browser...");
125+
const browser = await chromium.launch({ headless: true });
126+
const output = {};
127+
128+
// 1. Color Contrast (AA)
129+
console.log("Scanning: color-contrast...");
130+
try {
131+
const page = await browser.newPage();
132+
await page.setContent(colorContrastHTML);
133+
const result = await scanPage(page, {
134+
name: "Test", email: "test@test.com", pageTitle: "Color Contrast Test",
135+
});
136+
output['color-contrast'] = extractMessages(result, 'color-contrast');
137+
await page.close();
138+
} catch (e) { console.error("color-contrast error:", e.message); }
139+
140+
// 2. Color Contrast Enhanced (AAA)
141+
console.log("Scanning: color-contrast-enhanced...");
142+
try {
143+
const page = await browser.newPage();
144+
await page.setContent(colorContrastEnhancedHTML);
145+
const result = await scanPage(page, {
146+
name: "Test", email: "test@test.com", pageTitle: "Color Contrast Enhanced Test",
147+
ruleset: ['default', 'enable-wcag-aaa'],
148+
});
149+
output['color-contrast-enhanced'] = extractMessages(result, 'color-contrast-enhanced');
150+
await page.close();
151+
} catch (e) { console.error("color-contrast-enhanced error:", e.message); }
152+
153+
// 3. Target Size
154+
console.log("Scanning: target-size...");
155+
try {
156+
const page = await browser.newPage();
157+
await page.setContent(targetSizeHTML);
158+
const result = await scanPage(page, {
159+
name: "Test", email: "test@test.com", pageTitle: "Target Size Test",
160+
});
161+
output['target-size'] = extractMessages(result, 'target-size');
162+
await page.close();
163+
} catch (e) { console.error("target-size error:", e.message); }
164+
165+
// 4. Valid Lang
166+
console.log("Scanning: valid-lang...");
167+
try {
168+
const page = await browser.newPage();
169+
await page.setContent(validLangHTML);
170+
const result = await scanPage(page, {
171+
name: "Test", email: "test@test.com", pageTitle: "Valid Lang Test",
172+
});
173+
output['valid-lang'] = extractMessages(result, 'valid-lang');
174+
await page.close();
175+
} catch (e) { console.error("valid-lang error:", e.message); }
176+
177+
// 5. Readability (oobee-grading-text-contents)
178+
console.log("Scanning: oobee-grading-text-contents...");
179+
try {
180+
const page = await browser.newPage();
181+
await page.setContent(readabilityHTML);
182+
183+
// Simulate what the crawler does: extract text, grade readability
184+
const textContent = readabilityHTML.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
185+
const sentences = textContent.split(/(?<=[.!?])\s+/).filter(s => s.trim().length > 0);
186+
const flag = gradeReadability(sentences);
187+
console.log(` Readability flag: "${flag}"`);
188+
189+
if (flag) {
190+
const score = parseFloat(flag);
191+
let interpretation = '';
192+
if (score > 30) interpretation = 'It is targeted for junior college (JC) level comprehension and above.';
193+
else interpretation = 'It is targeted for university graduate level comprehension and above.';
194+
195+
output['oobee-grading-text-contents'] = [{
196+
category: 'needsReview',
197+
rule: 'oobee-grading-text-contents',
198+
description: 'Page content must use clear, plain language',
199+
items: [{
200+
html: '<html lang="en">...</html>',
201+
message: `Text content is potentially difficult to read. It scored ${flag} out of 50 on the Flesch-Kincaid Readability Test. ${interpretation}`,
202+
}],
203+
}];
204+
} else {
205+
console.log(" Score filtered out (<=0 or >50). No violation triggered.");
206+
}
207+
await page.close();
208+
} catch (e) { console.error("readability error:", e.message); }
209+
210+
await browser.close();
211+
212+
// Print results
213+
console.log("\n" + JSON.stringify(output, null, 2));
214+
})();

0 commit comments

Comments
 (0)