Accesibilidad: para todos, no opcional
La accesibilidad (abreviada a11y: "a", 11 letras, "y") es construir interfaces que cualquier persona pueda usar, incluidas las que navegan con lector de pantalla, solo con teclado, o con baja visión. No es un extra: es calidad de software (y, a menudo, un requisito legal).
La buena noticia: el 90 % de la accesibilidad es HTML bien hecho.
1) HTML semántico
Usa el elemento correcto para cada cosa. Un <button> ya es enfocable,
pulsable con Enter/Espacio y anunciado como botón; un <div onClick> no es nada
de eso.
// ❌ Mal: un div no es accesible por teclado ni se anuncia como botón
<div onClick={cerrar}>Cerrar</div>
// ✅ Bien
<button onClick={cerrar}>Cerrar</button>
Lo mismo con <nav>, <main>, <header>, <ul>/<li>, encabezados <h1>…<h6>
en orden: dan estructura que las tecnologías de asistencia entienden.
2) Etiquetas asociadas a sus campos
Todo control de formulario necesita una etiqueta asociada. La forma robusta es
<label htmlFor={id}> apuntando al id del campo. En JSX se escribe htmlFor
(no for, que es palabra reservada en JS), pero en el DOM real se convierte en el
atributo for.
<label htmlFor="email">Correo</label>
<input id="email" type="email" />
Al asociarlos: el lector de pantalla anuncia la etiqueta al enfocar el campo, y pulsar la etiqueta enfoca el input (mejor usabilidad para todos).
3) ARIA, solo cuando hace falta
Los atributos ARIA (aria-*) añaden información semántica que el HTML por sí
solo no expresa. La primera regla de ARIA es: no uses ARIA si un elemento
nativo ya hace el trabajo. Pero es muy útil para matices:
<input aria-required="true" aria-invalid={hayError} />
<button aria-label="Cerrar diálogo">✕</button> {/* botón con solo un icono */}
<nav aria-label="Principal">…</nav>
aria-labelda un nombre accesible cuando no hay texto visible (un botón con solo un icono).aria-required,aria-invalid,aria-expanded… comunican estado a las tecnologías de asistencia.
4) Foco y navegación por teclado
Mucha gente navega sin ratón. Asegúrate de que:
- Todo lo interactivo es enfocable y operable con teclado (usar
<button>,<a href>, inputs… lo da gratis). - El orden de tabulación sigue el orden visual.
- El foco es visible (no quites el
outlinesin poner una alternativa). - En componentes complejos (modales, menús) gestionas el foco con
tabIndex,onKeyDowny, si hace falta, moviendo el foco con unaref.
5) IDs únicos con useId
Asociar label e input por id tiene un problema en React: si el componente se
usa varias veces en la página, un id fijo ("email") se duplicaría, y
los ids deben ser únicos. La solución es el hook useId, que genera un
identificador único y estable (además funciona bien con SSR/hidratación):
function Campo({ etiqueta }) {
const id = React.useId();
return (
<p>
<label htmlFor={id}>{etiqueta}</label>
<input id={id} />
</p>
);
}
Importante:
useIdes para ids de accesibilidad (enlazar elementos), no para keys de listas. Reutiliza el mismo id (con sufijos si necesitas varios:${id}-error) para relacionar elementos del mismo componente.
En los ejercicios de este runtime, usa
React.useId()(con el prefijoReact.) para generar el id.