Accesibilidad web WCAG en aplicaciones a medida
Cómo implementar una estrategia de accesibilidad web WCAG en tu aplicación a medida
Llevo años entrando en proyectos donde la accesibilidad se trata como un parche al final del sprint, y siempre acaba igual: refactores caros, equipos frustrados y, lo peor, una parte importante de los usuarios fuera. Hoy la accesibilidad web es obligación legal en la Unión Europea —la Directiva Europea de Accesibilidad (EAA) se aplica plenamente desde junio de 2025— y, además, una ventaja competitiva tangible. Las aplicaciones accesibles llegan a más gente, funcionan mejor para todos y arrastran menos deuda técnica. Corregir tarde lo que se podía haber diseñado bien cuesta entre cinco y diez veces más. Esa cifra se repite en auditoría tras auditoría.
Aquí te cuento, paso a paso, cómo integrar WCAG 2.2 nivel AA en una aplicación web a medida: desde la auditoría inicial hasta las pruebas automatizadas dentro del pipeline de integración continua.
Por qué la accesibilidad es una decisión arquitectónica, no solo de diseño
Cuando relegas la accesibilidad a la fase de pruebas finales, encuentras lo de siempre: componentes propios sin roles ARIA, flujos que solo funcionan con ratón, formularios con etiquetas huérfanas, modales que atrapan el foco sin ruta de escape por teclado. No son bugs aislados. Son síntomas de decisiones de arquitectura tomadas meses antes, cuando nadie se hacía las preguntas correctas.
Tratar la accesibilidad como requisito arquitectónico implica tres cambios estructurales:
Selección del sistema de componentes. Cada pieza de la librería tiene que cumplir los patrones de WAI-ARIA Authoring Practices. Radix UI, React Aria o los propios elementos nativos (dialog, details, popover) son una base sólida porque ya gestionan roles, estados y navegación por teclado sin que tengas que reinventarlo.
Modelo de navegación y enrutamiento. Las SPA tienen un problema concreto: al cambiar de vista sin recargar, el lector de pantalla no se entera. La arquitectura debe contemplar la gestión del foco después de cada cambio de ruta —mover el foco al H1 de la nueva vista o a una región aria-live que anuncie el cambio— y actualizar el title del documento dinámicamente. Lo segundo se olvida más veces de las que parece.
Gestión del estado de la interfaz. Los estados de carga, error y vacío se comunican con regiones aria-live y la politeness adecuada: polite para actualizaciones informativas, assertive para errores críticos. Si tu app usa un store centralizado, conviene que las transiciones de estado incluyan los metadatos de accesibilidad que después consumirán los componentes de presentación. Así se evita que cada componente improvise su forma de anunciar lo que pasa.
Auditoría inicial: diagnóstico antes de escribir código
Antes de tocar nada, necesitas un mapa preciso de dónde estás. Una auditoría que funcione combina tres capas que se complementan.
Análisis automatizado. axe-core, Lighthouse o el validador del W3C atrapan entre el 30 y el 40 por ciento de los problemas: contrastes pobres, imágenes sin alt, jerarquía de encabezados rota, enlaces sin texto visible, formularios sin label. Pasar axe-core sobre todas las vistas te da un inventario clasificado por gravedad y criterio WCAG afectado. Útil, pero insuficiente por sí solo. Lo digo siempre que alguien me enseña un informe de Lighthouse con un 100 y cara de satisfecho.
Revisión manual con teclado y lector de pantalla. Un evaluador recorre la aplicación usando exclusivamente el teclado —tabulador, intro, escape, flechas— y comprueba que cada elemento interactivo es alcanzable, operable y muestra un foco visible. Después repite el recorrido con NVDA o VoiceOver para verificar nombres accesibles, anuncio de cambios dinámicos y orden de lectura coherente. Es el momento en el que aparecen los falsos positivos del análisis automático y, sobre todo, los falsos negativos.
Pruebas con usuarios reales. Las sesiones con personas que usan tecnologías de asistencia a diario detectan cosas que ni las herramientas ni los evaluadores expertos ven: flujos que cumplen la norma sobre el papel pero resultan confusos, instrucciones que dependen de pistas visuales que un usuario ciego no tiene forma de captar. Si puedes pagar a quienes participan, hazlo; su tiempo y su experiencia valen.
El entregable de la auditoría es un documento priorizado que vincula cada problema a un criterio WCAG concreto (por ejemplo, 1.4.3 Contraste mínimo, 2.1.1 Teclado, 4.1.2 Nombre, Función, Valor) y a los componentes del código que hay que tocar.
Implementación práctica: patrones de componentes accesibles
Con el diagnóstico delante, el trabajo se organiza alrededor de los componentes que más impacto tienen en una aplicación a medida.
Formularios. Cada campo necesita una etiqueta asociada programáticamente: label con for o aria-labelledby. Los mensajes de error se enlazan al campo con aria-describedby y se anuncian con aria-live. Los grupos de campos relacionados —dirección, datos de facturación— se envuelven en fieldset con su legend correspondiente. Los obligatorios llevan aria-required además del indicador visual; el asterisco solo no basta para nadie que no lo vea.
Navegación y menús. El menú principal vive dentro de un nav con aria-label descriptivo. Si hay varias regiones de navegación, cada una lleva una etiqueta diferente. Los desplegables siguen el patrón de menú de WAI-ARIA: el botón disparador indica su estado con aria-expanded, el menú recibe role="menu", cada opción role="menuitem", y las flechas arriba/abajo mueven el foco entre opciones. Implementarlo a mano es tedioso; por eso conviene apoyarse en una librería que ya lo haga bien.
Tablas de datos. Las tablas complejas, omnipresentes en aplicaciones de gestión, necesitan encabezados de fila y columna marcados con th y el atributo scope. Si permites ordenar columnas, el encabezado activo indica la dirección con aria-sort. Si hay filtrado dinámico, el número de resultados se anuncia tras cada filtro mediante una región aria-live; sin eso, un usuario de lector de pantalla filtra a ciegas.
Modales y diálogos. El elemento nativo dialog te resuelve gratis la gestión del foco y la trampa de foco. Si optas por un componente propio, tienes que replicar ese comportamiento sin atajos: al abrir, el foco pasa al primer interactivo o al propio diálogo; al cerrar, vuelve al disparador; Escape cierra; y el contenido del fondo se marca con aria-hidden="true" e inert para que no se pueda interactuar por error. Cada uno de esos puntos lo he visto fallar en producción.
Contenido dinámico y notificaciones. Los mensajes efímeros (toasts, snackbars) usan role="status" o role="alert" según la urgencia y permanecen visibles el tiempo suficiente para que alguien con dificultades cognitivas o motrices los lea y, si llevan acciones, las pulse. Una alternativa más robusta —y que yo prefiero— es mantener un registro de notificaciones accesible desde un punto fijo de la interfaz, para que nada importante se pierda por aparecer y desaparecer.
Automatización de pruebas de accesibilidad en el flujo de desarrollo
La accesibilidad se degrada si nadie la vigila. Integrar pruebas automatizadas en el pipeline evita regresiones y aligera las auditorías manuales periódicas.
Pruebas unitarias de componentes. jest-axe (con React Testing Library) o cypress-axe permiten ejecutar axe-core contra cada componente que ya estás renderizando en las pruebas existentes. Regla simple: ningún componente nuevo entra en main sin un test que verifique cero violaciones axe-core de nivel crítico y grave. Es una línea más en cada PR y ahorra semanas de remediación más adelante.
Pruebas de extremo a extremo. Playwright sirve para simular la navegación por teclado y comprobar que el foco sigue la secuencia esperada tras cada interacción. Un conjunto que recorra los flujos críticos —registro, inicio de sesión, creación de un recurso, edición, eliminación— detecta regresiones de teclado antes de que toquen producción.
Análisis estático. Reglas de eslint como eslint-plugin-jsx-a11y, o sus equivalentes en otros frameworks, marcan problemas en cuanto los escribes: imágenes sin alt, enlaces vacíos, onClick sin equivalente de teclado, atributos ARIA inválidos. Es la primera línea de defensa y la más barata.
Revisión de contraste y tipografía. axe-core comprueba el contraste de forma automática, pero conviene complementarlo con una revisión visual en la guía de estilos asegurando que los pares de color cumplen 4.5:1 para texto normal y 3:1 para texto grande. Los problemas de contraste suelen venir de combinaciones que el equipo de diseño no había previsto: hover sobre fondos claros, texto secundario en gris sobre gris.
El objetivo no es cobertura automatizada del 100 por ciento —eso, en accesibilidad, no existe—, sino atrapar las regresiones frecuentes y reservar el esfuerzo manual para lo que requiere juicio humano.
Cumplimiento legal y estándares: WCAG 2.2 y la normativa europea
El marco legal europeo en accesibilidad digital se sostiene sobre dos ejes.
La Directiva Europea de Accesibilidad (EAA, Directiva 2019/882) obliga a que los productos y servicios digitales comercializados en la UE sean accesibles desde junio de 2025. Entran sitios de comercio electrónico, banca, transporte y, en general, cualquier servicio digital dirigido al consumidor.
La norma EN 301 549 es el estándar técnico europeo de accesibilidad para productos y servicios TIC. Para contenido web remite directamente a WCAG 2.1 nivel AA, con la actualización a WCAG 2.2 esperada en próximas revisiones. A efectos prácticos, cumplir WCAG 2.2 AA es cumplir la norma europea.
Para una aplicación web a medida, el cumplimiento implica:
- Publicar una declaración de accesibilidad con el nivel de conformidad alcanzado, las limitaciones conocidas y un canal de contacto para que los usuarios reporten barreras.
- Mantener un registro de auditorías que documente el esfuerzo continuo de mejora.
- Establecer un proceso de respuesta a las reclamaciones, con plazos definidos para corregir las barreras reportadas.
En España, el Real Decreto 1112/2018 transpone la directiva europea para el sector público, y la Ley General de Derechos de las Personas con Discapacidad extiende obligaciones al sector privado en determinados supuestos. Las sanciones por incumplimiento pueden llegar a los 100.000 euros en los casos graves. No es un riesgo teórico.
Hoja de ruta para equipos que empiezan desde cero
Implementar accesibilidad en una aplicación ya en marcha parece abrumador, pero un enfoque incremental y bien priorizado produce resultados visibles en pocas semanas.
Semana 1-2: auditoría y formación. Ejecuta la auditoría automatizada y manual descrita arriba. Organiza una sesión práctica donde cada persona del equipo navegue la aplicación con teclado y lector de pantalla. La primera vez que un desarrollador escucha cómo suena su propio formulario en NVDA cambia bastantes cosas.
Semana 3-4: correcciones de alto impacto. Aborda lo que afecta a más usuarios con menos esfuerzo: contraste de colores, alt en imágenes, etiquetas de formulario, jerarquía de encabezados y foco visible. Integra eslint-plugin-jsx-a11y o equivalente y jest-axe en el CI.
Semana 5-8: refactorización de componentes clave. Reescribe los componentes más usados —menús, modales, tablas, formularios complejos— siguiendo WAI-ARIA Authoring Practices. Añade pruebas de extremo a extremo de navegación por teclado para los flujos principales.
Mes 3 en adelante: mejora continua. Revisión de accesibilidad en cada ciclo, auditorías manuales trimestrales y pruebas con usuarios con discapacidad al menos una vez al año. Es la cadencia mínima para que lo conseguido no se pierda.
Si tu equipo necesita orientación para definir la estrategia de accesibilidad de tu aplicación a medida o para integrar WCAG 2.2 en un proyecto en curso, contacta con nuestro equipo de consultoría técnica y diseñamos un plan adaptado a tu arquitectura y tus plazos.
La accesibilidad es proceso, no destino. Cada mejora beneficia a todos los usuarios —no solo a quienes usan tecnologías de asistencia— y protege a la organización frente a riesgos legales que cada año son más serios. El mejor momento para empezar fue al inicio del proyecto; el segundo mejor momento es esta semana.