Web accessibility (a11y) patterns and WCAG compliance. Covers WCAG 2.2 Level A/AA/AAA, ARIA attributes, keyboard navigation, screen reader support, color contrast, and semantic HTML. Trigger: When implementing accessibility features, when building accessible components, when ensuring WCAG compliance, when adding ARIA attributes, when handling keyboard navigation.
Resources
1Install
npx skillscat add dsantiagomj/dsmj-ai-toolkit/accessibility Install via the SkillsCat registry.
We need to produce a 2-3 sentence plain-text summary, objective, factual, no marketing language, no superlatives, no calls to action. No bullet points, no headings, no markdown. At most 60 words. Provide only the summary text. We need to capture: skill does: provides web accessibility patterns and WCAG compliance guidance, covering WCAG 2.2 levels, ARIA, keyboard navigation, screen reader support, color contrast, semantic HTML. Problem it solves: ensures accessible web components, helps meet WCAG compliance, avoid inaccessible UI.
Accessibility - WCAG Compliance & A11y Best Practices
Build inclusive web experiences for all users
When to Use This Skill
Use this skill when:
- Implementing accessible components
- Ensuring WCAG compliance
- Adding ARIA attributes
- Handling keyboard navigation
- Supporting screen readers
- Checking color contrast
Critical Patterns
Pattern 1: Semantic HTML
When: Building accessible structure
Good:
<!-- ✅ Semantic elements -->
<header>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
</header>
<main>
<article>
<h1>Article Title</h1>
<p>Content...</p>
</article>
</main>Bad:
<!-- ❌ Divs for everything -->
<div class="header">
<div class="nav">
<div onclick="navigate()">Home</div>
</div>
</div>Why: Semantic HTML provides structure for screen readers and enables automatic keyboard navigation.
Pattern 2: Keyboard Navigation
When: Making interactive elements accessible
Good:
<!-- ✅ Button is focusable -->
<button onClick={handleClick}>Click me</button>
<!-- ✅ Div with proper keyboard support -->
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleClick();
}
}}
>
Click me
</div>Bad:
<!-- ❌ Non-focusable div -->
<div onclick="handleClick()">Click me</div>Why: All interactive elements must be keyboard accessible. Use native elements when possible.
Pattern 3: ARIA Labels
When: Providing accessible names
Good:
<!-- ✅ aria-label for icon buttons -->
<button aria-label="Close dialog">
<svg><!-- X icon --></svg>
</button>
<!-- ✅ aria-labelledby for dialogs -->
<h2 id="dialog-title">Confirm Delete</h2>
<div role="dialog" aria-labelledby="dialog-title">
<p>Are you sure?</p>
</div>Bad:
<!-- ❌ No accessible name -->
<button>
<svg><!-- X icon --></svg>
</button>Why: Screen readers need text labels to understand interactive elements.
Pattern 4: Form Accessibility
When: Building accessible forms
Good:
<!-- ✅ Explicit label association -->
<label htmlFor="email">Email *</label>
<input
id="email"
type="email"
aria-required="true"
aria-invalid={hasError}
aria-describedby={hasError ? "email-error" : undefined}
/>
{hasError && (
<div id="email-error" role="alert">
Please enter a valid email
</div>
)}Bad:
<!-- ❌ No label -->
<input type="email" placeholder="Email" />Why: Labels are required for screen readers. Error messages must be announced.
Pattern 5: Color Contrast
When: Ensuring readability
Good:
/* ✅ Meets AA (7:1) */
.text {
color: #595959;
background: #ffffff;
}Bad:
/* ❌ Fails AA (4.5:1) */
.text {
color: #777777;
background: #ffffff;
}Why: WCAG AA requires 4.5:1 for normal text, 3:1 for large text.
Code Examples
Example 1: Accessible Form with Validation
export function SignupForm() {
const [errors, setErrors] = useState<Record<string, string>>({});
return (
<form onSubmit={handleSubmit}>
<div className="space-y-1">
<label htmlFor="email" className="block text-sm font-medium">
Email Address *
</label>
<input
id="email"
type="email"
aria-required="true"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? 'email-error' : undefined}
className={cn(
'border rounded-lg px-3 py-2',
errors.email && 'border-red-500'
)}
/>
{errors.email && (
<p id="email-error" role="alert" className="text-sm text-red-600">
{errors.email}
</p>
)}
</div>
<button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded-lg">
Sign Up
</button>
</form>
);
}Example 2: Keyboard-Accessible Custom Dropdown
export function Dropdown({ items, onSelect }: DropdownProps) {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button
onClick={() => setIsOpen(!isOpen)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setIsOpen(!isOpen);
}
}}
aria-haspopup="listbox"
aria-expanded={isOpen}
>
Select Option
</button>
{isOpen && (
<ul role="listbox" tabIndex={-1}>
{items.map((item, index) => (
<li
key={item.id}
role="option"
tabIndex={0}
onClick={() => onSelect(item)}
onKeyDown={(e) => {
if (e.key === 'Enter') onSelect(item);
}}
>
{item.label}
</li>
))}
</ul>
)}
</div>
);
}For comprehensive examples and detailed implementations, see the references/ folder.
Quick Reference
WCAG AA Requirements
- ✅ 4.5:1 text contrast (3:1 for large text)
- ✅ 3:1 UI component contrast
- ✅ All functionality keyboard accessible
- ✅ Visible focus indicators
- ✅ Text resizable to 200%
- ✅ Form inputs labeled
- ✅ Meaningful alt text
- ✅ Logical heading structure
Component States
- Default
- Hover
- Focus (keyboard)
- Active
- Disabled
- Error
Progressive Disclosure
For detailed implementations:
- WCAG Compliance - Complete WCAG 2.2 requirements, ARIA patterns, form accessibility
- Keyboard Navigation - Tab order, focus management, keyboard shortcuts
References
Maintained by dsmj-ai-toolkit