Accessibility
Portable engineering policies for coding agents — git, testing, logging, and language conventions written once and referenced everywhere
npx -y skills add andr-ca/agentharness --skill accessibilityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 25 days oldThe repository was created 25 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when building or reviewing web UIs for accessibility — WCAG 2.2 Level AA compliance, semantic HTML over ARIA, keyboard navigation, color contrast, screen reader patterns, and testing approach.
SKILL.md
4.7 KB, as published. Nobody here has run it
Accessibility
This skill is self-contained for day-to-day use. Deeper reference (needs
the full harness checkout): patterns/accessibility/README.md (full
examples including ARIA live regions and APG patterns). External
canonical sources: WCAG 2.2 and the
WAI-ARIA Authoring Practices.
Target: WCAG 2.2 Level AA.
The first rule: don't use ARIA if a native element exists
A native <button>, <a href>, <input>, <label>, <nav>, or
<table> brings keyboard behavior, focus management, and the correct
role/state for free. role="button" + tabindex + keyboard handler is
a liability you now own forever.
<!-- Good: browser handles focus, Enter, Space, and role automatically -->
<button type="button" onclick="save()">Save</button>
<!-- Avoid: you must re-implement everything the native button provides -->
<div role="button" tabindex="0" onclick="save()" onkeydown="handleKey(event)">Save</div>
Perceivable
- Text alternatives. Meaningful images:
altdescribing purpose. Decorative images:alt="". Icon-only controls:aria-labelor visually-hidden text. - Contrast. Body text ≥ 4.5:1 against background. Large text (≥ 24px, or ≥ 18.66px bold) and UI components/graphics ≥ 3:1. Verify computed colors — don't eyeball.
- Color alone is not enough. A red border and an error text/icon — not red alone.
Operable
- Keyboard. All interactive elements must be reachable by Tab, in
logical order, with no keyboard trap.
tabindex="0"for natural order;tabindex="-1"for programmatic-only focus; never positive values. - Visible focus. Never
outline: nonewithout a clearly visible replacement. Keyboard users navigate by it. - Skip link. First focusable element on pages with repeated
navigation:
<a href="#main">Skip to main content</a>. - Target size. Interactive targets ≥ 24×24 px (WCAG 2.2 §2.5.8), with adequate spacing if smaller.
Understandable
- Label every input.
<label for>oraria-labelledby. Placeholder text is not a label — it disappears when the user starts typing. - Errors in text. On validation failure: describe what's wrong and
how to fix it; associate via
aria-describedby; setaria-invalid="true"on the field. - Consistent, predictable behavior. Focus doesn't jump unexpectedly; forms don't submit on focus change.
Robust
- Name, Role, Value for every custom widget. Follow the matching ARIA APG pattern exactly — its roles, states, and keyboard interactions. A half-implemented ARIA widget is worse than a native element.
- Announce dynamic changes. Content that updates without a page load
(toasts, async results, validation) needs an
aria-liveregion (politefor status updates,assertiveonly for urgent/time-sensitive changes) orrole="alert".
<!-- Status message that updates asynchronously -->
<div aria-live="polite" aria-atomic="true" class="sr-only" id="status">
<!-- Updated by JS: "3 items saved." -->
</div>
Testing: automated is not enough
Automated tools (axe-core, Lighthouse) catch ~30–50% of WCAG issues —
contrast, missing labels/alt, invalid ARIA. The rest requires manual
testing:
- Tab through every interactive element — confirm logical order, visible focus, no traps.
- Run a screen reader (NVDA + Firefox, VoiceOver + Safari) on each flow. Hear, don't just read, what's announced.
- Check at 200% browser zoom — layout must not break or lose content.
- Run
axe-corein CI as a smoke test, not a complete audit.
Pitfalls to catch in review
// Missing alt — screen reader announces the file path
<img src="/hero.png" /> // WRONG
<img src="/hero.png" alt="Developer reviewing code" /> // RIGHT
// Form input with no label
<input type="email" placeholder="[email protected]" /> // WRONG
<label htmlFor="email">Email</label>
<input type="email" id="email" placeholder="[email protected]" /> // RIGHT
// Click handler on non-interactive element — keyboard users can't trigger
<div onClick={handleSave}>Save</div> // WRONG
<button type="button" onClick={handleSave}>Save</button> // RIGHT
// outline: none without a replacement focus style
button:focus { outline: none; } // WRONG
button:focus-visible { outline: 2px solid #005fcc; outline-offset: 2px; } // RIGHT