Membangun Komponen Accessible
Component library yang accessible dari awal menghemat waktu jauh lebih banyak daripada fix di akhir. Prinsipnya: semantic HTML first, ARIA only when needed, keyboard always.
Accessible Button
// ✅ Gunakan <button>, bukan <div onClick>
function Button({ children, onClick, disabled, loading }) {
return (
<button
onClick={onClick}
disabled={disabled || loading}
aria-busy={loading}
>
{loading ? (
<>
<span aria-hidden="true">⏳</span>
<span className="sr-only">Memproses...</span>
</>
) : children}
</button>
);
}
Accessible Modal
function Modal({ isOpen, onClose, title, children }) {
const modalRef = useRef();
useEffect(() => {
if (isOpen) {
// Save previous focus
const prev = document.activeElement;
// Focus modal
modalRef.current?.focus();
// Restore on close
return () => prev?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
ref={modalRef}
tabIndex={-1}
onKeyDown={(e) => e.key === "Escape" && onClose()}
>
<h2 id="modal-title">{title}</h2>
{children}
<button onClick={onClose}>Tutup</button>
</div>
);
}
Accessible Form
<form>
<div>
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
required
aria-describedby="email-hint email-error"
aria-invalid={hasError}
/>
<p id="email-hint">Contoh: [email protected]</p>
{hasError && <p id="email-error" role="alert">Email tidak valid</p>}
</div>
</form>
Libraries yang Membantu
- Radix UI — Unstyled, fully accessible primitives (dialog, dropdown, tabs)
- React Aria (Adobe) — Hooks for accessible components
- Headless UI — From Tailwind team, accessible + unstyled
Checklist per Component
- Keyboard operable? (Tab, Enter, Space, Escape, Arrow keys)
- Focus visible?
- Screen reader announces state changes?
- Color contrast meets WCAG AA (4.5:1 text, 3:1 large text)?
- Works at 200% zoom?