{"summary":{"total":156,"passed":142,"failed":14,"level":"AA","score":91},"byCategory":{"images":{"passed":45,"failed":3},"headings":{"passed":28,"failed":2},"contrast":{"passed":52,"failed":5},"navigation":{"passed":17,"failed":4}},"issues":[{"id":"img-alt-missing","wcag":"1.1.1","level":"A","impact":"critical","description":"Image missing alt text","location":{"file":"docs/guide/setup.md","line":42,"element":"<img src=\"diagram.png\">"},"suggestion":"Add descriptive alt text: alt=\"System architecture diagram showing...\""},{"id":"heading-skip","wcag":"1.3.1","level":"A","impact":"moderate","description":"Heading levels should only increase by one","location":{"file":"docs/api/users.md","line":15,"element":"<h4>User Properties</h4>"},"context":"H2 -> H4 (skipped H3)","suggestion":"Change to <h3> or add missing <h3> above"},{"id":"color-contrast","wcag":"1.4.3","level":"AA","impact":"serious","description":"Text does not meet contrast ratio requirements","location":{"file":"docs/_static/custom.css","line":28,"element":".note { color: #999; }"},"details":{"foreground":"#999999","background":"#ffffff","ratio":"2.85:1","required":"4.5:1"},"suggestion":"Change to #767676 or darker for 4.5:1 ratio"}],"wcagCompliance":{"A":{"passed":48,"failed":6},"AA":{"passed":35,"failed":8},"AAA":{"passed":12,"failed":0}}}
WCAG Guidelines Checked
Perceivable (Principle 1)
1.1.1 - Non-text Content:-Imageshavealttext-Decorativeimageshaveemptyalt-Compleximageshavelongdescriptions-Iconshaveaccessiblenames1.3.1 - Info and Relationships:-Headingsproperlystructured-Listsproperlymarkedup-Tableshaveheaders-Formlabelsassociated1.4.1 - Use of Color:-Colornotsoleindicator-Linksdistinguishable1.4.3 - Contrast (Minimum):-Text:4.5:1ratio-Large text:3:1ratio-UI components:3:1ratio
3.1.1 - Language of Page:-langattributepresent3.2.3 - Consistent Navigation:-Navigationconsistentacrosspages3.3.2 - Labels or Instructions:-Forminputshavelabels
Image Alt Text Validation
Alt Text Rules
const altTextRules = {
// Must have alt attributerequired: {
test: (img) => img.hasAttribute('alt'),
message: 'Image must have alt attribute'
},
// Alt text should be descriptivedescriptive: {
test: (img) => {
const alt = img.getAttribute('alt');
const badPatterns = [
/^image$/i,
/^photo$/i,
/^picture$/i,
/^graphic$/i,
/\.(?:png|jpg|gif|svg)$/i,
/^untitled/i
];
return !badPatterns.some(p => p.test(alt));
},
message: 'Alt text should describe the image content'
},
// Not too longlength: {
test: (img) => {
const alt = img.getAttribute('alt');
return alt.length <= 125;
},
message: 'Alt text should be concise (under 125 characters)'
},
// Decorative images should have empty altdecorative: {
test: (img) => {
if (img.hasAttribute('role') && img.getAttribute('role') === 'presentation') {
return img.getAttribute('alt') === '';
}
returntrue;
},
message: 'Decorative images should have empty alt=""'
}
};
Alt Text Suggestions
functionsuggestAltText(imagePath, context) {
const suggestions = [];
// Based on filenameconst filename = path.basename(imagePath, path.extname(imagePath));
if (filename.includes('diagram')) {
suggestions.push(`Diagram showing ${extractContext(context)}`);
}
if (filename.includes('screenshot')) {
suggestions.push(`Screenshot of ${extractContext(context)}`);
}
if (filename.includes('logo')) {
suggestions.push(`${extractBrand(filename)} logo`);
}
// Based on surrounding textconst heading = findNearestHeading(context);
if (heading) {
suggestions.push(`Illustration for ${heading}`);
}
return suggestions;
}