by @cap-go
Accessibility guide for Capacitor apps covering screen readers, semantic HTML, focus management, and WCAG compliance. Use this skill when users need to make their app accessible.
Build inclusive apps that work for everyone.
// Accessible button
<button
aria-label="Delete item"
aria-describedby="delete-hint"
>
<TrashIcon />
</button>
<span id="delete-hint" className="sr-only">
Permanently removes this item
</span>
// Accessible input
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
aria-required="true"
aria-invalid={hasError}
aria-describedby={hasError ? "email-error" : undefined}
/>
{hasError && <span id="email-error">Invalid email</span>}
// Announce dynamic content
<div aria-live="polite" aria-atomic="true">
{message}
</div>
// Urgent announcements
<div aria-live="assertive" role="alert">
{error}
</div>
/* Minimum 44x44pt */
button, a, input {
min-height: 44px;
min-width: 44px;
}
/* Icon buttons need padding */
.icon-button {
padding: 12px;
}
/* Good contrast (4.5:1 for text) */
.text {
color: #333333;
background: #ffffff;
}
/* Don't rely on color alone */
.error {
color: #d32f2f;
border-left: 4px solid #d32f2f; /* Visual indicator */
}
.error::before {
content: "⚠ "; /* Icon indicator */
}
// Move focus after navigation
useEffect(() => {
const heading = document.querySelector('h1');
heading?.focus();
}, [page]);
// Trap focus in modals
function trapFocus(element: HTMLElement) {
const focusable = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const first = ...