Agent skill
web-accessibility
Web accessibility and interface standards guide. Covers WCAG compliance, semantic HTML, keyboard navigation, screen readers, forms, touch targets, and internationalization. Use when building, reviewing, or auditing web interfaces for accessibility and UX quality.
Install this agent skill to your Project
npx add-skill https://github.com/s-hiraoku/synapse-a2a/tree/main/plugins/synapse-a2a/skills/web-accessibility
SKILL.md
Web Accessibility
Build interfaces that work for everyone. These are not optional enhancements β they are baseline quality.
Semantic HTML
Use the right element for the job. Never simulate interactive elements with <div>.
// BAD: div with click handler
<div onClick={handleClick} className="button">Submit</div>
// GOOD: semantic button
<button onClick={handleClick}>Submit</button>
// BAD: div as link
<div onClick={() => router.push('/about')}>About</div>
// GOOD: anchor/Link for navigation
<Link href="/about">About</Link>
Element Selection Guide
| Purpose | Element | Not |
|---|---|---|
| Action (submit, toggle, delete) | <button> |
<div onClick> |
| Navigation to URL | <a> / <Link> |
<button onClick={navigate}> |
| Form input | <input>, <select>, <textarea> |
Custom div-based inputs |
| Section heading | <h1>β<h6> (sequential) |
<div className="heading"> |
| List of items | <ul> / <ol> + <li> |
Repeated <div> |
| Navigation group | <nav> |
<div className="nav"> |
| Main content | <main> |
<div id="content"> |
Keyboard Navigation
Every interactive element must be keyboard accessible.
Focus Management
/* NEVER remove focus indicators without replacement */
/* BAD */
*:focus { outline: none; }
/* GOOD: Visible focus only on keyboard navigation */
.interactive:focus-visible {
outline: 2px solid var(--color-accent);
outline-offset: 2px;
}
/* Group focus for compound controls */
.input-group:focus-within {
outline: 2px solid var(--color-accent);
}
Keyboard Event Handling
// Interactive custom elements need keyboard support
function CustomButton({ onClick, children }: { onClick: () => void; children: React.ReactNode }) {
return (
<div
role="button"
tabIndex={0}
onClick={onClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
onClick();
}
}}
>
{children}
</div>
);
}
// Better: just use <button> and avoid all of the above
Skip Links
Provide skip navigation for keyboard users.
<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50">
Skip to main content
</a>
Headings used as scroll targets need offset for fixed headers:
[id] { scroll-margin-top: 5rem; }
ARIA Patterns
Icon Buttons
// Icon-only buttons MUST have aria-label
<button aria-label="Close dialog" onClick={onClose}>
<XIcon aria-hidden="true" />
</button>
// Decorative icons are hidden from screen readers
<span aria-hidden="true">π</span> Secure connection
Live Regions
Announce dynamic content changes to screen readers.
// Toast notifications
<div role="status" aria-live="polite">
{notification && <p>{notification.message}</p>}
</div>
// Error alerts
<div role="alert" aria-live="assertive">
{error && <p>{error.message}</p>}
</div>
Loading States
<button disabled={isLoading} aria-busy={isLoading}>
{isLoading ? 'Saving\u2026' : 'Save'} {/* proper ellipsis character */}
</button>
// Skeleton screens
<div aria-busy="true" aria-label="Loading content">
<Skeleton />
</div>
Forms
Labels
Every input must have an associated label.
// GOOD: Explicit association
<label htmlFor="email">Email</label>
<input id="email" type="email" autoComplete="email" />
// GOOD: Wrapping (clickable label, no htmlFor needed)
<label>
Email
<input type="email" autoComplete="email" />
</label>
// GOOD: Visually hidden but accessible
<label htmlFor="search" className="sr-only">Search</label>
<input id="search" type="search" placeholder="Search..." />
Input Types and Autocomplete
Use semantic input types to get the right mobile keyboard and browser behavior.
<input type="email" autoComplete="email" />
<input type="tel" autoComplete="tel" />
<input type="url" autoComplete="url" />
<input type="password" autoComplete="current-password" />
<input type="password" autoComplete="new-password" />
Validation and Errors
<div>
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? 'email-error' : undefined}
/>
{errors.email && (
<p id="email-error" role="alert" className="text-red-600">
{errors.email}
</p>
)}
</div>
Form Behavior Rules
- Never prevent paste on any input
- Disable spellcheck on emails and codes:
spellCheck={false} - Submit button stays enabled until request starts; show spinner during loading
- Focus first error on submit failure
- Checkboxes and radio buttons: single hit target, no dead zones between label and input
Images and Media
// Informative images: descriptive alt
<img src="chart.png" alt="Revenue grew 40% from Q1 to Q3 2025" />
// Decorative images: empty alt
<img src="divider.svg" alt="" />
// Prevent layout shift: always set dimensions
<img src="photo.jpg" width={800} height={600} alt="Team photo" />
// Below fold: lazy load
<img src="photo.jpg" loading="lazy" alt="..." />
// Critical: prioritize
<img src="hero.jpg" fetchPriority="high" alt="..." />
Touch and Mobile
Touch Targets
Minimum 44x44px for all interactive elements (WCAG 2.5.5).
.touch-target {
min-height: 44px;
min-width: 44px;
}
Safe Areas
Handle device notches and home indicators.
.full-bleed {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
Touch Behavior
/* Prevent 300ms delay and highlight flash */
.interactive {
touch-action: manipulation;
-webkit-tap-highlight-color: transparent;
}
/* Prevent scroll chaining on modals */
.modal { overscroll-behavior: contain; }
Internationalization
// BAD: Hardcoded formats
const date = `${month}/${day}/${year}`;
const price = `$${amount.toFixed(2)}`;
// GOOD: Locale-aware formatting
const date = new Intl.DateTimeFormat(locale).format(new Date());
const price = new Intl.NumberFormat(locale, {
style: 'currency',
currency: 'USD',
}).format(amount);
// Detect language
const lang = request.headers.get('accept-language')?.split(',')[0] ?? 'en';
Performance for Accessibility
- Lists > 50 items: virtualize
- Critical fonts:
<link rel="preload" as="font">withfont-display: swap - Avoid layout reads during render (causes jank for screen reader users too)
- Uncontrolled inputs perform better than controlled for large forms
Checklist
Use this for review:
- All interactive elements are keyboard accessible
- Focus indicators are visible on
:focus-visible - Color contrast meets WCAG AA (4.5:1 body, 3:1 large text)
- Images have appropriate alt text
- Form inputs have labels
- Error messages are associated with inputs via
aria-describedby - Icon-only buttons have
aria-label - Dynamic content uses
aria-liveregions - Touch targets are minimum 44x44px
-
prefers-reduced-motionis respected - No
outline: nonewithout replacement focus style - Heading hierarchy is sequential (no skipped levels)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
task-planner
Guide for decomposing large tasks into a structured plan with dependency chains, managing priorities, and distributing work across agents. Outputs plan cards or delegation messages as the team contract; TodoList for personal micro-steps.
react-performance
Comprehensive React and Next.js performance optimization guide. Covers waterfall elimination, bundle size reduction, server-side optimization, re-render prevention, and rendering performance. Use when building, reviewing, or optimizing React/Next.js applications for speed.
release
Update version in pyproject.toml, plugin.json, and add changelog entry. This skill should be used when the user wants to bump the version number and update CHANGELOG.md. Triggered by /release or /version commands.
api-design
Guide API design for REST, GraphQL, gRPC, and CLI interfaces. Use this skill when designing new APIs, reviewing existing API contracts, or establishing API conventions for a project. Produces consistent, well-documented API specifications.
pr-guardian
Continuously monitor a GitHub PR for merge conflicts, CI failures, and CodeRabbit review comments, then automatically fix any issues found. Polls every 5 minutes and loops until every check is green. Use this skill whenever a PR has just been created or code has been pushed to a PR branch β it should be the default follow-up action after any PR creation or push. Also trigger on: "watch this PR", "guard this PR", "monitor CI", "keep fixing until green", "PRγη£θ¦γγ¦", "CIγιγγΎγ§ η΄γγ¦", /pr-guardian. When a PostToolUse hook reports that a push or PR creation just happened, proactively invoke this skill to start monitoring without waiting for the user to ask.
post-impl2
Workflow: Test workflow with non-existent agent target. . Triggered by /post-impl2 command.
Didn't find tool you were looking for?