From harness-claude
Scans React/Vue/Svelte/HTML UI components for WCAG AA accessibility violations like missing alt text and ARIA roles, generates reports by design strictness, applies automated fixes.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> WCAG compliance verification and remediation. Scan components for accessibility violations, evaluate severity against design strictness, generate actionable reports, and apply automated fixes for mechanical issues.
Reviews web UI components, pages, and code for WCAG 2.1/2.2 AA accessibility issues like color contrast, keyboard navigation, ARIA, and semantics. Prioritizes and fixes critical problems one at a time.
Audits StyleSeed-based UI components and pages for WCAG 2.2 AA issues like contrast, touch targets, semantics, and focus, applying safe code fixes.
Share bugs, ideas, or general feedback.
WCAG compliance verification and remediation. Scan components for accessibility violations, evaluate severity against design strictness, generate actionable reports, and apply automated fixes for mechanical issues.
on_new_feature triggers fire and the feature includes UI componentson_project_init triggers fire to establish an accessibility baselineLoad design tokens. Read design-system/tokens.json (if it exists) to identify declared color values and contrast pairs. Token-defined colors are the source of truth -- hardcoded colors in components are themselves a violation.
Read design strictness. Check harness.config.json for design.strictness:
strict -- all findings are errors that block (CI fails, PR cannot merge)standard -- warnings are visible, errors block (default behavior)permissive -- all findings are informational (nothing blocks, but everything is reported)2.5. Check for i18n skill overlap. Read harness.config.json for i18n.enabled:
i18n.enabled: true, defer lang and dir attribute checks to harness-i18n. Do not scan for missing lang on <html> or missing dir on user-content containers -- those checks are covered by the i18n skill's scan phase with more context (locale-aware, RTL-aware).i18n.enabled is false or absent, scan for lang/dir as normal (these remain part of the accessibility audit).Scan component files. Search all files matching .tsx, .jsx, .vue, .svelte, .html for the following violations:
Images and media:
<img> tags without alt attribute (A11Y-001)<img> tags with empty alt="" on non-decorative images (A11Y-002)<video> and <audio> without captions/transcripts (A11Y-003)ARIA and semantics:
<button>, <a>, <input>) without accessible labels (A11Y-010)aria-label or visually hidden text (A11Y-011)<div> or <span> without role="button" and keyboard handler (A11Y-012)role attributes on custom interactive widgets (A11Y-013)aria-hidden="true" on focusable elements (A11Y-014)Heading structure:
<h1> followed by <h3>, skipping <h2>) (A11Y-020)<h1> elements on a single page/component (A11Y-021)A11Y-022)Color and contrast:
A11Y-030)A11Y-031)Keyboard navigation:
onClick handlers without corresponding onKeyDown/onKeyUp (A11Y-040)tabIndex on custom interactive elements (A11Y-041)tabIndex values (disrupts natural tab order) (A11Y-042):focus or :focus-visible styles) (A11Y-043)Forms:
<input>, <select>, <textarea> without associated <label> or aria-label (A11Y-050)id attributes on form controls (needed for label association) (A11Y-051)aria-invalid on validation states (A11Y-052)Load anti-pattern catalogs. Read additional detection rules from agents/skills/shared/design-knowledge/ if available. These catalogs contain industry-specific accessibility patterns (e.g., healthcare forms require higher contrast, fintech requires screen reader-compatible data tables).
Record all findings. Each finding includes:
A11Y-001)Assign severity based on design.strictness:
strict mode: all violations are error severitystandard mode: missing alt, missing labels, contrast failures are error; heading order, tabIndex are warn; informational patterns are infopermissive mode: contrast failures and missing labels are warn; everything else is infoCalculate contrast ratios. For every color pair found in scanned code:
L = 0.2126 * R + 0.7152 * G + 0.0722 * B (where R, G, B are linearized sRGB values)(L1 + 0.05) / (L2 + 0.05) where L1 is the lighter colorCross-reference with design tokens. If design-system/tokens.json exists:
Check graph constraints. If a graph exists at .harness/graph/, use DesignConstraintAdapter from packages/graph/src/constraints/DesignConstraintAdapter.ts to:
VIOLATES edges (violations already recorded in the graph)VIOLATES edges for findings from this scandesign.strictness to control which violations produce edgesCategorize findings. Group into categories:
Generate summary header:
Accessibility Report
====================
Scanned: 42 component files
Findings: 18 total (6 error, 8 warn, 4 info)
Strictness: standard
List findings grouped by category. Each finding follows this format:
A11Y-001 [error] Missing alt attribute on <img>
File: src/components/UserAvatar.tsx
Line: 24
Element: <img src={user.avatarUrl} className="avatar" />
WCAG: 1.1.1 Non-text Content
Fix: Add alt={user.name} or alt="" if decorative
A11Y-031 [error] Contrast ratio 2.8:1 fails WCAG AA (requires 4.5:1)
File: src/components/Button.tsx
Line: 15
Element: color: #999 on background: #fff
WCAG: 1.4.3 Contrast (Minimum)
Fix: Use color token "neutral.600" (#475569, ratio 4.9:1) instead
Provide category summaries with counts and severity breakdown.
List actionable next steps:
This phase is optional. It applies fixes only for mechanical issues -- violations with a single, unambiguous correct fix. Subjective issues (color choices, layout decisions, content writing) are never auto-fixed.
Fixable violations:
A11Y-001: Add alt="" to <img> tags that are decorative (inside <button>, <a>, or with role="presentation")A11Y-011: Add aria-label to icon-only <button> elements (using the icon name as label)A11Y-012: Add role="button" and tabIndex={0} to clickable <div> elementsA11Y-041: Add tabIndex={0} to custom interactive elements missing itA11Y-051: Generate id attributes for form controls and link them to labelsApply each fix as a minimal, targeted edit. Use the Edit tool. Do not refactor surrounding code. Do not change formatting. The fix should be the smallest possible change that resolves the violation.
Show before/after diff for each fix. Present the exact change to the user. This is a hard gate -- no fix is applied without showing the diff first.
Re-scan after fixes. Run the scan phase again on fixed files to confirm violations are resolved. Report:
Do NOT fix:
harness validate -- Accessibility findings surface as design constraint violations when design.strictness is strict or standard. Running validate after a scan reflects the current a11y state.harness scan -- Refresh the knowledge graph after fixes to update VIOLATES edges. Ensures impact analysis stays current.DesignConstraintAdapter (packages/graph/src/constraints/DesignConstraintAdapter.ts) -- Reads design.strictness from project config to control violation severity. Manages VIOLATES edges in the graph for design and accessibility constraints.DesignIngestor (packages/graph/src/ingest/DesignIngestor.ts) -- Provides token data used for contrast checking. The ingestor parses tokens.json so the accessibility scanner can compare code colors against declared tokens.harness-impact-analysis -- When tokens change (palette update, new colors), impact analysis traces affected components. The accessibility skill uses this to determine which components need re-scanning.harness-design-system -- Dependency. When contrast failures originate from token definitions (not component code), escalate to harness-design-system to fix at the source.harness-i18n deduplication -- When i18n.enabled: true in config, lang and dir attribute checks are deferred to the i18n skill. This prevents duplicate findings across the accessibility and i18n reports. When i18n is not enabled, these checks remain part of the accessibility scan.error, warn, info)harness validate reflects accessibility findings at the configured strictness levelContext: A React component DashboardCard.tsx with known accessibility issues.
Source file:
// src/components/DashboardCard.tsx
export function DashboardCard({ title, value, icon, onClick }) {
return (
<div className="card" onClick={onClick}>
<img src={icon} />
<h3>{title}</h3>
<span style={{ color: '#999', fontSize: '14px' }}>{value}</span>
</div>
);
}
SCAN findings:
A11Y-001 [error] Missing alt attribute on <img>
File: src/components/DashboardCard.tsx
Line: 5
Element: <img src={icon} />
WCAG: 1.1.1 Non-text Content
A11Y-012 [error] Clickable <div> without role="button" and keyboard handler
File: src/components/DashboardCard.tsx
Line: 4
Element: <div className="card" onClick={onClick}>
WCAG: 2.1.1 Keyboard
A11Y-031 [warn] Contrast ratio 2.8:1 for #999 on #fff fails WCAG AA
File: src/components/DashboardCard.tsx
Line: 7
Element: <span style={{ color: '#999' }}>
WCAG: 1.4.3 Contrast (Minimum)
Note: Hardcoded color -- not from token set
A11Y-030 [info] Hardcoded color value not from design token set
File: src/components/DashboardCard.tsx
Line: 7
Element: color: '#999'
FIX phase (auto-fixable only):
- <img src={icon} />
+ <img src={icon} alt="" />
- <div className="card" onClick={onClick}>
+ <div className="card" role="button" tabIndex={0} onClick={onClick} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') onClick?.(); }}>
Remaining (requires human judgment):
A11Y-031: Contrast failure -- fix requires choosing a darker color. Escalate to design tokens or get human input on replacement color.A11Y-001: The alt="" fix assumes decorative. If the icon conveys meaning, human must write descriptive alt text.| Rationalization | Reality |
|---|---|
| "The contrast ratio is 4.4:1 — that's essentially 4.5:1 and the difference is imperceptible. I'll mark it passing." | WCAG AA requires exactly 4.5:1 for normal text. 4.4:1 fails. There is no rounding or visual perception exception in the standard. Flag the failure with the actual ratio and let the user decide how to remediate. |
"This <div onClick> already has good visual styling — adding role='button' and a keyboard handler is unnecessary clutter." | A clickable <div> without role="button" and onKeyDown is inaccessible to keyboard-only users and screen reader users. Visual styling has no bearing on ARIA semantics or keyboard reachability. This is A11Y-012, always flagged. |
"The automated fix for this <img> alt attribute is obvious — I'll apply it without showing the diff since it's just adding alt=''." | Every automated fix must be presented as a before/after diff before being written to disk. This is a hard gate. The correct alt value for non-decorative images requires human judgment, and even alt="" makes a semantic claim about decorativeness that must be confirmed. |
"I18n is enabled, so I'll skip the lang and dir attribute checks entirely — harness-i18n will catch them." | Deferral to harness-i18n is conditional on i18n.enabled: true in config. If i18n is not configured, these checks remain part of this skill's scan. Always read the config before skipping any check category. |
| "There are 15 findings in this component — I'll fix the easy ones automatically and leave the rest without reporting them explicitly." | All findings must be reported, regardless of whether they are auto-fixable. The report is the primary deliverable of the REPORT phase. Selectively reporting only fixable violations hides the full accessibility debt from the team. |
These are hard stops. Violating any gate means the process has broken down.
error-severity contrast violations, not zero findings overall.design.strictness config specifies. If the project is in strict mode, a missing alt attribute is an error. The scanner does not get to decide it is a warning.design.strictness is not configured: Default to standard mode. Report: "No design.strictness found in harness.config.json. Using 'standard' (warnings visible, errors block). Set design.strictness in config to customize."