Documentation accessibility validation and remediation. Check WCAG 2.1 compliance, validate alt text, analyze heading hierarchy, verify color contrast, and generate accessibility reports.
Audits documentation for WCAG 2.1 compliance, validates alt text, checks heading structure, and generates accessibility reports.
npx claudepluginhub a5c-ai/babysitterThis skill is limited to using the following tools:
README.mdDocumentation accessibility validation and remediation.
Invoke this skill when you need to:
| Parameter | Type | Required | Description |
|---|---|---|---|
| inputPath | string | Yes | Path to documentation or built site |
| action | string | Yes | audit, validate-images, check-headings |
| standard | string | No | WCAG level (A, AA, AAA) |
| outputFormat | string | No | json, html, sarif |
| fix | boolean | No | Auto-fix issues where possible |
{
"inputPath": "./docs/_build/html",
"action": "audit",
"standard": "AA",
"outputFormat": "json"
}
{
"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 }
}
}
1.1.1 - Non-text Content:
- Images have alt text
- Decorative images have empty alt
- Complex images have long descriptions
- Icons have accessible names
1.3.1 - Info and Relationships:
- Headings properly structured
- Lists properly marked up
- Tables have headers
- Form labels associated
1.4.1 - Use of Color:
- Color not sole indicator
- Links distinguishable
1.4.3 - Contrast (Minimum):
- Text: 4.5:1 ratio
- Large text: 3:1 ratio
- UI components: 3:1 ratio
2.1.1 - Keyboard:
- All functionality keyboard accessible
- No keyboard traps
- Skip links present
2.4.1 - Bypass Blocks:
- Skip navigation link
- Landmark regions
2.4.2 - Page Titled:
- Descriptive page titles
2.4.6 - Headings and Labels:
- Descriptive headings
- Clear labels
2.4.7 - Focus Visible:
- Visible focus indicators
3.1.1 - Language of Page:
- lang attribute present
3.2.3 - Consistent Navigation:
- Navigation consistent across pages
3.3.2 - Labels or Instructions:
- Form inputs have labels
const altTextRules = {
// Must have alt attribute
required: {
test: (img) => img.hasAttribute('alt'),
message: 'Image must have alt attribute'
},
// Alt text should be descriptive
descriptive: {
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 long
length: {
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 alt
decorative: {
test: (img) => {
if (img.hasAttribute('role') && img.getAttribute('role') === 'presentation') {
return img.getAttribute('alt') === '';
}
return true;
},
message: 'Decorative images should have empty alt=""'
}
};
function suggestAltText(imagePath, context) {
const suggestions = [];
// Based on filename
const 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 text
const heading = findNearestHeading(context);
if (heading) {
suggestions.push(`Illustration for ${heading}`);
}
return suggestions;
}
function analyzeHeadings(content) {
const headings = extractHeadings(content);
const issues = [];
let lastLevel = 0;
headings.forEach((heading, index) => {
const level = heading.level;
// Check for skipped levels
if (level > lastLevel + 1 && lastLevel !== 0) {
issues.push({
type: 'heading-skip',
heading: heading.text,
line: heading.line,
expected: lastLevel + 1,
actual: level
});
}
// Check for multiple H1s
if (level === 1 && index > 0) {
issues.push({
type: 'multiple-h1',
heading: heading.text,
line: heading.line
});
}
lastLevel = level;
});
return {
structure: buildHeadingTree(headings),
issues
};
}
function getContrastRatio(foreground, background) {
const fgLuminance = getRelativeLuminance(foreground);
const bgLuminance = getRelativeLuminance(background);
const lighter = Math.max(fgLuminance, bgLuminance);
const darker = Math.min(fgLuminance, bgLuminance);
return (lighter + 0.05) / (darker + 0.05);
}
function meetsContrastRequirement(ratio, isLargeText, level = 'AA') {
const requirements = {
'AA': { normal: 4.5, large: 3 },
'AAA': { normal: 7, large: 4.5 }
};
const threshold = isLargeText
? requirements[level].large
: requirements[level].normal;
return ratio >= threshold;
}
async function analyzeStylesheet(cssPath) {
const css = await fs.readFile(cssPath, 'utf8');
const ast = postcss.parse(css);
const issues = [];
ast.walkDecls('color', (decl) => {
const rule = decl.parent;
const bgColor = findBackgroundColor(rule) || '#ffffff';
const fgColor = decl.value;
const ratio = getContrastRatio(fgColor, bgColor);
if (!meetsContrastRequirement(ratio, false, 'AA')) {
issues.push({
selector: rule.selector,
foreground: fgColor,
background: bgColor,
ratio: ratio.toFixed(2),
line: decl.source.start.line,
suggestion: suggestAccessibleColor(fgColor, bgColor)
});
}
});
return issues;
}
async function testKeyboardNavigation(page) {
const issues = [];
// Get all focusable elements
const focusable = await page.$$('a, button, input, select, textarea, [tabindex]');
for (const element of focusable) {
await element.focus();
// Check focus visibility
const hasFocusStyle = await page.evaluate((el) => {
const styles = window.getComputedStyle(el);
const focusStyles = window.getComputedStyle(el, ':focus');
return (
styles.outline !== 'none' ||
styles.boxShadow !== 'none' ||
focusStyles.outline !== 'none'
);
}, element);
if (!hasFocusStyle) {
issues.push({
type: 'focus-not-visible',
element: await element.evaluate(el => el.outerHTML.substring(0, 100))
});
}
}
return issues;
}
{
"devDependencies": {
"axe-core": "^4.8.0",
"pa11y": "^6.2.0",
"lighthouse": "^11.0.0",
"puppeteer": "^21.0.0",
"color-contrast-checker": "^2.1.0"
}
}
# Run axe-core audit
npx axe ./docs/_build/html --rules wcag2aa
# Run pa11y
npx pa11y https://docs.example.com --standard WCAG2AA
# Lighthouse accessibility audit
npx lighthouse https://docs.example.com --only-categories=accessibility
# Check single page
npx axe https://docs.example.com/guide --save report.json
Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.
Search, retrieve, and install Agent Skills from the prompts.chat registry using MCP tools. Use when the user asks to find skills, browse skill catalogs, install a skill for Claude, or extend Claude's capabilities with reusable AI agent components.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.