Back to UI/UX Design

accessibility-compliance

WCAGaccessibilityARIAinclusive designscreen readerskeyboard navigationUI/UXcompliancefront-end
36.8k📄 MIT🕒 2026-06-16Source ↗

Install this skill

npx skills add wshobson/agents

Works across Claude Code, Cursor, Codex, Copilot & Antigravity

The accessibility-compliance skill automates the integration of WCAG 2.2 standards into frontend components. It evaluates existing UI code for compliance gaps, injects necessary ARIA attributes, establishes correct keyboard focus sequences, and ensures interactive elements meet minimum touch target dimensions. By applying this skill, developers transform static interfaces into keyboard-navigable, screen-reader-ready applications. It specifically targets the implementation of complex interaction patterns—such as modal focus traps and dynamic live regions—that often break during manual development. This skill acts as a technical advisor that bridges the gap between design requirements and valid HTML/ARIA implementation, ensuring that visual components provide equivalent information and functionality to assistive technology users without requiring a deep audit manual from scratch.

When to Use This Skill

  • Ensuring custom UI components pass WCAG 2.2 audit requirements
  • Fixing navigation flows for users who rely exclusively on keyboard input
  • Adding support for screen readers like VoiceOver or TalkBack to complex apps
  • Refining touch target sizes for mobile-first user interfaces

How to Invoke This Skill

Example prompts that trigger this skill in Claude Code, Cursor, or Antigravity:

  • Make this modal accessible for screen readers
  • Add keyboard navigation and focus trapping to this dropdown
  • Verify WCAG 2.2 compliance for this component
  • Ensure this button meets minimum touch target sizes
  • Add ARIA labels to this form to improve accessibility

Pro Tips

  • 💡Always test with actual assistive technologies (e.g., NVDA, JAWS, VoiceOver) and real users where possible, as automated tools can only catch a subset of issues.
  • 💡Prioritize semantic HTML and native elements before resorting to ARIA, as proper semantics provide a robust and accessible baseline.
  • 💡Integrate accessibility checks into your CI/CD pipeline to catch regressions early and maintain compliance throughout the development lifecycle.

What this skill does

  • Injecting ARIA roles, states, and properties into standard DOM elements
  • Applying focus management logic to modals and interactive dialogs
  • Auditing and resizing touch targets to meet 24x24px and 44x44px minimums
  • Structuring semantic HTML for proper screen reader heading hierarchy
  • Configuring keyboard trap loops for focus-sensitive UI components

When not to use it

  • For creating aesthetic-only design systems without functional UI components
  • When you need a full legal certification or third-party accessibility audit certificate

Example workflow

  1. Analyze the component source code for missing semantic elements or ARIA attributes
  2. Apply required ARIA roles and labels to non-native interactive elements
  3. Integrate focus-trap logic to prevent focus from leaving active modals
  4. Adjust CSS dimensions to ensure touch targets meet minimum criteria
  5. Validate the tab order to match the visual focus sequence

Prerequisites

  • Basic understanding of semantic HTML
  • React or standard DOM-based UI library
  • Existing UI component code

Pitfalls & limitations

  • !Over-using ARIA roles where semantic HTML would suffice
  • !Forgetting to handle the 'Escape' key for closing modals
  • !Inconsistent focus-ring visibility across browser engines

FAQ

Does this skill provide a full accessibility audit?
It helps implement and fix specific compliance issues, but it should be paired with manual testing and automated linting tools for a complete audit.
Can it fix contrast ratio issues?
Yes, it identifies contrast violations, but you must manually update the hex codes or theme variables in your CSS to meet the 4.5:1 ratio.
How does it handle dynamic content changes?
It utilizes ARIA live regions to announce updates to screen readers automatically when content refreshes without a page reload.
Does it work with non-React projects?
The logic is portable, but the provided patterns use React-based syntax for state and focus management.

How it compares

Unlike generic prompts that offer broad advice, this skill applies specific code-level patterns for ARIA and focus trapping, directly modifying your component source to achieve measurable WCAG compliance.

Source & trust

37k stars📄 MIT🕒 Updated 2026-06-16
📄 Full skill instructions — original source: wshobson/agents
# 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

## 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,
},
};


## Best Practices

1. **Use Semantic HTML**: Prefer native elements over ARIA when possible
2. **Test with Real Users**: Include people with disabilities in user testing
3. **Keyboard First**: Design interactions to work without a mouse
4. **Don't Disable Focus Styles**: Style them, don't remove them
5. **Provide Text Alternatives**: All non-text content needs descriptions
6. **Support Zoom**: Content should work at 200% zoom
7. **Announce Changes**: Use live regions for dynamic content
8. **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)

## Resources

- [WCAG 2.2 Guidelines](https://www.w3.org/WAI/WCAG22/quickref/)
- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/)
- [A11y Project Checklist](https://www.a11yproject.com/checklist/)
- [Inclusive Components](https://inclusive-components.design/)
- [Deque University](https://dequeuniversity.com/)

How to Use This Skill Unit

Option A: Project-Specific (Recommended)

  1. Click "Download" above
  2. In your project, create the directory: .agent/skills/accessibility-compliance/
  3. Save the file as SKILL.md
  4. The agent will automatically discover the skill based on its description.

Option B: Global Installation (All Agents)

Save the file to these locations to make it available across all projects:

  • Claude Code: ~/.claude/skills/wshobson/agents/accessibility-compliance/SKILL.md
  • Cursor: ~/.cursor/skills/wshobson/agents/accessibility-compliance/SKILL.md
  • Antigravity: ~/.gemini/antigravity/skills/wshobson/agents/accessibility-compliance/SKILL.md

🚀 Install with CLI:
npx skills add wshobson/agents

Read the Master Guide: Mastering Agent Skills

Recommended Rules

View more rules

Recommended Workflows

View more workflows

Recommended MCP Servers

View more MCP servers

Take It Further

Maximize your productivity with these powerful resources

📋

Define Your Standards

Set up coding standards to ensure this workflow produces consistent, high-quality results.

Browse Rules Library
📖

Master Workflows

Learn how to create custom workflows, use Turbo Mode, and build your automation library.

Complete Guide

How to use this Skill in Claude Code & Cursor

For Claude Code (CLI)

To use this skill in Claude Code, copy the rule content into your project's custom instructions or follow our Add-Skill CLI guide. This ensures Claude follows your standards during every code generation.

For Cursor & Windsurf

For Cursor or Windsurf, individual skills are best used in the "Rules for AI" section. This specific unit helps the agent avoid ui/ux design issues, leading to cleaner, more efficient code.

Why the skill format matters: the standardized Agent Skills format lets your AI agent load detailed instructions only when they are relevant, keeping your prompt clean while improving results.

Source & attribution

This skill is categorized under UI/UX Design and is published by W. Shobson, maintained in wshobson/agents.

← Browse All Agent Skills
Sponsored AI assistant. Recommendations may be paid.