Accessibility Compliance
Seth HobsonCommunityCreated by Seth Hobson
- Skill
- Guidelines
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured approach to building inclusive digital experiences. It covers WCAG 2.2 compliance, ARIA patterns, keyboard navigation, and mobile accessibility. It emphasizes semantic HTML, proper focus management, and assistive technology support to ensure applications are perceivable, operable, understandable, and robust for all users.
What it does
- Provides patterns for WCAG 2.2 compliant UI components like buttons, modals, and tabs.
- Explains the correct usage of ARIA roles, states, and properties.
- Outlines requirements for keyboard navigation and focus management.
- Details mobile accessibility standards including touch target sizing and gesture support.
- Offers a checklist for WCAG 2.2 success criteria.
- Suggests manual and automated testing tools for accessibility audits.
When to use it
- When designing or developing new web components that require accessibility compliance.
- When auditing an existing application for WCAG 2.2 violations.
- When implementing complex interactive widgets like accordions or dialogs.
- When optimizing interfaces for screen readers and keyboard-only users.
Advantages
- Promotes inclusive design by prioritizing semantic HTML.
- Reduces common accessibility pitfalls like keyboard traps and missing labels.
- Provides ready-to-use code patterns for accessible UI elements.
- Ensures compliance with modern accessibility standards.
Usage examples
- “How do I create an accessible modal dialog?”
- The skill provides a pattern using focus trapping, Escape key handling, and proper ARIA labeling to ensure the modal is fully accessible to screen readers and keyboard users.
- “What are the requirements for mobile touch targets?”
- The skill specifies a minimum touch target size of 44x44dp to ensure ease of interaction for all users on mobile devices.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/wshobson/accessibility-compliance/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip accessibility-compliance.zip -d .agents/skills
Documentation by Seth Hobson
Accessibility Compliance
Master accessibility implementation to create inclusive experiences that work for everyone, including users with disabilities.
When to Use This Skill
Implementing WCAG 2.2 Level AA or AAA compliance
Building screen reader accessible interfaces
Adding keyboard navigation to interactive components
Implementing focus management and focus trapping
Creating accessible forms with proper labeling
Supporting reduced motion and high contrast preferences
Building mobile accessibility features (iOS VoiceOver, Android TalkBack)
Conducting accessibility audits and fixing violations
Detailed patterns and worked examples
Detailed pattern documentation lives in references/details.md. Read that file when the navigation tier above is insufficient.
Best Practices
Use Semantic HTML: Prefer native elements over ARIA when possible
Test with Real Users: Include people with disabilities in user testing
Keyboard First: Design interactions to work without a mouse
Don't Disable Focus Styles: Style them, don't remove them
Provide Text Alternatives: All non-text content needs descriptions
Support Zoom: Content should work at 200% zoom
Announce Changes: Use live regions for dynamic content
Respect Preferences: Honor prefers-reduced-motion and prefers-contrast
Common Issues
Missing alt text: Images without descriptions
Poor color contrast: Text hard to read against background
Keyboard traps: Focus stuck in component
Missing labels: Form inputs without associated labels
Auto-playing media: Content that plays without user initiation
Inaccessible custom controls: Recreating native functionality poorly
Missing skip links: No way to bypass repetitive content
Focus order issues: Tab order doesn't match visual order
Testing Tools
Automated: axe DevTools, WAVE, Lighthouse
Manual: VoiceOver (macOS/iOS), NVDA/JAWS (Windows), TalkBack (Android)
Simulators: NoCoffee (vision), Silktide (various disabilities)
Files
- ATTRIBUTION.mdAdded by AutoGineer990 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md2.3 KiB
- references/aria-patterns.md12.9 KiB
- references/details.md9.4 KiB
- references/mobile-accessibility.md12.8 KiB
- references/wcag-guidelines.md13.9 KiB
references/details.md
accessibility-compliance — detailed patterns and worked examples
Core Capabilities
1. WCAG 2.2 Guidelines
Perceivable: Content must be presentable in different ways
Operable: Interface must be navigable with keyboard and assistive tech
Understandable: Content and operation must be clear
Robust: Content must work with current and future assistive technologies
2. ARIA Patterns
Roles: Define element purpose (button, dialog, navigation)
States: Indicate current condition (expanded, selected, disabled)
Properties: Describe relationships and additional info (labelledby, describedby)
Live regions: Announce dynamic content changes
3. Keyboard Navigation
Focus order and tab sequence
Focus indicators and visible focus states
Keyboard shortcuts and hotkeys
Focus trapping for modals and dialogs
4. Screen Reader Support
Semantic HTML structure
Alternative text for images
Proper heading hierarchy
Skip links and landmarks
5. Mobile Accessibility
Touch target sizing (44x44dp minimum)
VoiceOver and TalkBack compatibility
Gesture alternatives
Dynamic Type support
Quick Reference
WCAG 2.2 Success Criteria Checklist
| Level | Criterion | Description |
|---|---|---|
| A | 1.1.1 | Non-text content has text alternatives |
| A | 1.3.1 | Info and relationships programmatically determinable |
| A | 2.1.1 | All functionality keyboard accessible |
| A | 2.4.1 | Skip to main content mechanism |
| AA | 1.4.3 | Contrast ratio 4.5:1 (text), 3:1 (large text) |
| AA | 1.4.11 | Non-text contrast 3:1 |
| AA | 2.4.7 | Focus visible |
| AA | 2.5.8 | Target size minimum 24x24px (NEW in 2.2) |
| AAA | 1.4.6 | Enhanced contrast 7:1 |
| AAA | 2.5.5 | Target size minimum 44x44px |
Key Patterns
Pattern 1: Accessible Button
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary";
isLoading?: boolean;
}
function AccessibleButton({
children,
variant = "primary",
isLoading = false,
disabled,
...props
}: ButtonProps) {
return (
<button
// Disable when loading
disabled={disabled || isLoading}
// Announce loading state to screen readers
aria-busy={isLoading}
// Describe the button's current state
aria-disabled={disabled || isLoading}
className={cn(
// Visible focus ring
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
// Minimum touch target size (44x44px)
"min-h-[44px] min-w-[44px]",
variant === "primary" && "bg-primary text-primary-foreground",
(disabled || isLoading) && "opacity-50 cursor-not-allowed",
)}
{...props}
>
{isLoading ? (
<>
<span className="sr-only">Loading</span>
<Spinner aria-hidden="true" />
</>
) : (
children
)}
</button>
);
}Pattern 2: Accessible Modal Dialog
import * as React from "react";
import { FocusTrap } from "@headlessui/react";
interface DialogProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}
function AccessibleDialog({ isOpen, onClose, title, children }: DialogProps) {
const titleId = React.useId();
const descriptionId = React.useId();
// Close on Escape key
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape" && isOpen) {
onClose();
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [isOpen, onClose]);
// Prevent body scroll when open
React.useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
}
return () => {
document.body.style.overflow = "";
};
}, [isOpen]);
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby={titleId}
aria-describedby={descriptionId}
>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50"
aria-hidden="true"
onClick={onClose}
/>
{/* Focus trap container */}
<FocusTrap>
<div className="fixed inset-0 flex items-center justify-center p-4">
<div className="bg-background rounded-lg shadow-lg max-w-md w-full p-6">
<h2 id={titleId} className="text-lg font-semibold">
{title}
</h2>
<div id={descriptionId}>{children}</div>
<button
onClick={onClose}
className="absolute top-4 right-4"
aria-label="Close dialog"
>
<X className="h-4 w-4" />
</button>
</div>
</div>
</FocusTrap>
</div>
);
}Pattern 3: Accessible Form
function AccessibleForm() {
const [errors, setErrors] = React.useState<Record<string, string>>({});
return (
<form aria-describedby="form-errors" noValidate>
{/* Error summary for screen readers */}
{Object.keys(errors).length > 0 && (
<div
id="form-errors"
role="alert"
aria-live="assertive"
className="bg-destructive/10 border border-destructive p-4 rounded-md mb-4"
>
<h2 className="font-semibold text-destructive">
Please fix the following errors:
</h2>
<ul className="list-disc list-inside mt-2">
{Object.entries(errors).map(([field, message]) => (
<li key={field}>
<a href={`#${field}`} className="underline">
{message}
</a>
</li>
))}
</ul>
</div>
)}
{/* Required field with error */}
<div className="space-y-2">
<label htmlFor="email" className="block font-medium">
Email address
<span aria-hidden="true" className="text-destructive ml-1">
*
</span>
<span className="sr-only">(required)</span>
</label>
<input
id="email"
name="email"
type="email"
required
aria-required="true"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? "email-error" : "email-hint"}
className={cn(
"w-full px-3 py-2 border rounded-md",
errors.email && "border-destructive",
)}
/>
{errors.email ? (
<p id="email-error" className="text-sm text-destructive" role="alert">
{errors.email}
</p>
) : (
<p id="email-hint" className="text-sm text-muted-foreground">
We'll never share your email.
</p>
)}
</div>
<button type="submit" className="mt-4">
Submit
</button>
</form>
);
}Pattern 4: Skip Navigation Link
function SkipLink() {
return (
<a
href="#main-content"
className={cn(
// Hidden by default, visible on focus
"sr-only focus:not-sr-only",
"focus:absolute focus:top-4 focus:left-4 focus:z-50",
"focus:bg-background focus:px-4 focus:py-2 focus:rounded-md",
"focus:ring-2 focus:ring-primary",
)}
>
Skip to main content
</a>
);
}
// In layout
function Layout({ children }) {
return (
<>
<SkipLink />
<header>...</header>
<nav aria-label="Main navigation">...</nav>
<main id="main-content" tabIndex={-1}>
{children}
</main>
<footer>...</footer>
</>
);
}Pattern 5: Live Region for Announcements
function useAnnounce() {
const [message, setMessage] = React.useState("");
const announce = React.useCallback(
(text: string, priority: "polite" | "assertive" = "polite") => {
setMessage(""); // Clear first to ensure re-announcement
setTimeout(() => setMessage(text), 100);
},
[],
);
const Announcer = () => (
<div
role="status"
aria-live="polite"
aria-atomic="true"
className="sr-only"
>
{message}
</div>
);
return { announce, Announcer };
}
// Usage
function SearchResults({ results, isLoading }) {
const { announce, Announcer } = useAnnounce();
React.useEffect(() => {
if (!isLoading && results) {
announce(`${results.length} results found`);
}
}, [results, isLoading, announce]);
return (
<>
<Announcer />
<ul>{/* results */}</ul>
</>
);
}Color Contrast Requirements
// Contrast ratio utilities
function getContrastRatio(foreground: string, background: string): number {
const fgLuminance = getLuminance(foreground);
const bgLuminance = getLuminance(background);
const lighter = Math.max(fgLuminance, bgLuminance);
const darker = Math.min(fgLuminance, bgLuminance);
return (lighter + 0.05) / (darker + 0.05);
}
// WCAG requirements
const CONTRAST_REQUIREMENTS = {
// Normal text (<18pt or <14pt bold)
normalText: {
AA: 4.5,
AAA: 7,
},
// Large text (>=18pt or >=14pt bold)
largeText: {
AA: 3,
AAA: 4.5,
},
// UI components and graphics
uiComponents: {
AA: 3,
},
};Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Seth Hobson
- Publisher
- Seth Hobson (Community)
- Source
- wshobson/agents · plugins/ui-design/skills/accessibility-compliance · commit 4bde316
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Seth Hobson.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download accessibility-compliance.zip