Loading...
Loading...
Accessibility (a11y) best practices for React components. Use when creating UI components, forms, interactive elements, or reviewing code for accessibility compliance.
npx skill4agent add sergiodxa/agent-skills frontend-accessibility-best-practices// Bad: divs with class names
<div className="header">...</div>
<div className="nav">...</div>
<div className="content">...</div>
// Good: semantic elements
<header>...</header>
<nav aria-label={t("Primary")}>...</nav>
<main>...</main>
<footer>...</footer>// Icon-only buttons need accessible labels
<Button variant="icon" onPress={onClose}>
<XMarkIcon aria-hidden="true" />
<span className="sr-only">{t("Close")}</span>
</Button>
// Visually hidden section headings
<section>
<h2 className="sr-only">{t("Search results")}</h2>
<SearchResultsList />
</section>// Error messages - announced immediately
{
error && (
<p role="alert" className="text-failure-600">
{error}
</p>
);
}
// Status updates - announced politely
<div role="status" aria-live="polite">
{t("{{count}} results found", { count })}
</div>;// Bad: div with onClick not keyboard accessible
<div onClick={handleClick}>Click me</div>
// Good: button has Enter/Space support
<button onClick={handleClick}>Click me</button>
// Good: react-aria Button handles everything
import { Button } from "react-aria-components";
<Button onPress={handlePress}>Click me</Button>// Always use focus-visible for focus styles
<button className="focus-visible:ring-2 focus-visible:ring-teal-600">
Click me
</button>;
// react-aria Modal handles focus trapping automatically
import { Modal, Dialog } from "react-aria-components";
<Modal isOpen={isOpen}>
<Dialog>{/* Focus automatically trapped here */}</Dialog>
</Modal>;import { usePrefersReducedMotion } from "~/hooks/use-prefers-reduced-motion";
// CSS approach
<div className="animate-bounce motion-reduce:animate-none">
Bouncing content
</div>;
// JS approach
function AnimatedCounter({ value }) {
let prefersReducedMotion = usePrefersReducedMotion();
if (prefersReducedMotion) return <span>{value}</span>;
return <CountUp target={value} />;
}// Icon buttons need explicit sizing
<Button variant="icon" className="h-11 w-11">
<XMarkIcon className="h-5 w-5" />
<span className="sr-only">{t("Close")}</span>
</Button>
// Links need padding for tappable area
<Link to={href} className="block py-3 px-4">
{label}
</Link>app/components/heading.tsxapp/hooks/use-prefers-reduced-motion.tsapp/components/field/field.tsx