Accesibilidad Frontend
Se aplica a componentes en <ui-packages-dir> y páginas en <web-dir>/src/. El objetivo es WCAG 2.1 Nivel AA, y es un piso, no una meta.
Resolución de convenciones del proyecto
Los valores entre paréntesis angulares provienen del perfil de la pila del proyecto. Resuélvelos en este orden:
- Lee
STACK-PROFILE.mden la raíz del repositorio y usa sus valores textualmente. - Si está ausente o falta una clave, infiere del repositorio —
css-strategyyui-frameworkde las dependencias de la aplicación web. - Si aún no se resuelve, coincide con la convención dominante ya presente en el repositorio.
- Solo cuando el repositorio está vacío, recurre al valor predeterminado indicado en este skill — luego escribe los valores elegidos en
STACK-PROFILE.md.
Nunca inventes una nueva convención cuando el repositorio ya exprese una.
Reglas estrictas
1. HTML semántico primero
<button>para acciones,<a>para navegación. Nunca un<div>con un manejador de clic.- Los niveles de encabezado descienden sin saltar.
<label>para cada campo de formulario, asociado conhtmlFor.<fieldset>y<legend>para controles agrupados.- Puntos de referencia —
<nav>,<main>,<footer>,<aside>— en cada página.
2. ARIA solo cuando HTML no puede
El orden de preferencia es elemento semántico, luego atributo ARIA, luego comportamiento personalizado. Nunca uses ARIA para anular la semántica que un elemento ya tiene — un role="button" en un <div> reimplementa, mal, lo que <button> da gratis.
ARIA incorrecto es peor que ninguno: un aria-label erróneo reemplaza silenciosamente el nombre accesible que un lector de pantalla calcularía correctamente.
3. La operación con teclado es obligatoria
- Todo lo interactivo es alcanzable con Tab.
- El orden de tabulación sigue el orden visual.
- El foco siempre es visible. Nunca elimines un contorno sin reemplazarlo.
Escapecierra diálogos y ventanas emergentes;EnteryEspacioactivan controles; las teclas de flecha se mueven dentro de widgets compuestos.- Los widgets personalizados gestionan el foco explícitamente.
4. Color y contraste
- 4.5:1 para texto normal, 3:1 para texto grande (18px, o 14px negrita) y para límites de interfaz significativos.
- El color nunca transmite significado por sí solo — acompáñalo con texto, un icono o un patrón.
- Se verifican ambos temas, no solo el predeterminado.
5. Imágenes y medios
- Cada imagen tiene
alt. Descriptivo para contenido,alt=""para decoración. - Nunca "imagen", "foto" o el nombre del archivo.
- Las imágenes complejas obtienen una descripción larga mediante
aria-describedbyo un pie de foto. - El video lleva subtítulos; el audio lleva una transcripción.
6. Formularios
- Cada entrada etiquetada.
- Errores vinculados con
aria-describedbyy anunciados conrole="alert". - Campos no válidos marcados con
aria-invalid. - Campos obligatorios marcados con el atributo
required— y si se muestra un asterisco, su significado está indicado en texto en algún lugar. - En un envío fallido, el foco se mueve al primer campo no válido.
7. Tamaño objetivo
WCAG 2.1 AA no tiene un tamaño objetivo mínimo (2.2 AA establece 24×24 CSS px; la cifra de 44×44 es 2.1 AAA y la convención HIG de la plataforma). Trata 44×44 como el piso para acciones primarias en superficies táctiles, y aplícalo de la siguiente manera:
- Acciones primarias y destructivas, y cualquier cosa a la que un usuario acceda en un teléfono: 44×44.
- Elementos densos de escritorio — barras de herramientas, acciones de filas de tablas, controles de formulario compactos — pueden ser más pequeños, hasta 24×24, siempre que los objetivos no estén adyacentes a otros objetivos y exista un control de tamaño completo equivalente en otro lugar.
- El espacio cuenta. Un control de 32px con 6px de espacio libre a cada lado cumple con el criterio 2.2; un control de 32px pegado a su vecino no.
Las bibliotecas de componentes externas suelen estar por debajo de este piso — una altura de botón predeterminada de 32 o 36 CSS px es común. No edites manualmente el archivo externo para corregirlo, y no trates la discrepancia como aceptable por defecto. Aumenta el valor predeterminado en <wrappers-pkg> para que todos los consumidores hereden el tamaño compatible, y mantén los tamaños más pequeños disponibles como opciones explícitas para contextos densos. De esa manera, la opción accesible es la que obtienes sin pensar en ello.
Registra qué resolución tomó el proyecto en STACK-PROFILE.md. Una exención no declarada se redescubre como un error cada seis meses.
8. Movimiento
- Respeta
prefers-reduced-motion. - El contenido que se reproduce automáticamente tiene un control de pausa.
- Nada parpadea más de tres veces por segundo.
9. Las pruebas son parte del trabajo
- Opera la función solo con el teclado.
- Escúchala con un lector de pantalla — VoiceOver en macOS, NVDA en Windows.
- Ejecuta un análisis automatizado en la suite de extremo a extremo.
Las herramientas automatizadas detectan aproximadamente un tercio de los problemas reales. Son un piso, no una aprobación.
Patrones
Las implementaciones completas están en references/component-patterns.md. Lo que garantiza cada patrón:
- Botón — un
<button>semántico cuyo anillo de foco se muestra para usuarios de teclado pero no para clics de ratón (focus-visible), con transiciones seguras para el movimiento y una propiedadariaLabelpara uso solo con icono. - Entrada de formulario — una entrada etiquetada cuyo texto de error está vinculado programáticamente con
aria-describedby, anunciado conrole="alert", y marcado conaria-invalid; el asterisco obligatorio esaria-hidden. - Diálogo — un
<dialog>nativo usandoshowModal()para captura de foco gratuita, manejo deEscapey fondo inerte; el foco se mueve al abrir y se restaura al activador al cerrar. - Listbox — un selector personalizado con navegación de flechas rotatoria y cableado ARIA correcto (
aria-activedescendant,role="listbox"); constrúyelo solo cuando un<select>nativo no sea adecuado. - Enlace de salto — permite a los usuarios de teclado saltar la navegación, visible solo cuando está enfocado; el objetivo lleva
tabIndex={-1}para que el foco realmente llegue allí.
Referencia rápida de WCAG
| Principio | Requisito | En la práctica |
|---|---|---|
| Perceptible | Contenido disponible para cada sentido | Texto alternativo, subtítulos, color más icono |
| Operable | Todo alcanzable con teclado | Orden de tabulación, Escape cierra, foco visible |
| Comprensible | Predecible, redactado claramente | Navegación coherente, lenguaje sencillo |
| Robusto | Funciona con tecnología de asistencia | HTML válido, ARIA correcto, elementos semánticos |
| Problema | Solución |
|---|---|
| Entrada sin etiqueta | Añadir <label htmlFor> |
| Bajo contraste | Cumplir 4.5:1, o 3:1 para texto grande |
| Sin ruta de teclado | Elementos interactivos reales, foco gestionado |
| Sin texto alternativo | Describirlo, o alt="" si es decorativo |
| Significado solo con color | Añadir texto o un icono |
| Botón solo con icono | Añadir aria-label |
| Contenido parpadeante | Mantener menos de tres parpadeos por segundo |
Preferencias de movimiento
Maneja el movimiento reducido en CSS plano. Una variante de clase de utilidad como motion-safe: solo se aplica cuando el movimiento está permitido, por lo que ponerla dentro de una consulta de movimiento reducido produce una regla que nunca puede coincidir.
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
Cerca de cero en lugar de none mantiene los eventos de fin de transición activos, por lo que los componentes que esperan en ellos no se cuelgan.
Para control por componente, aplica la variante motion-safe: a la utilidad animada misma — motion-safe:transition-colors, como en el Botón anterior.
Contenido visualmente oculto
La mayoría de los marcos de utilidades ya incluyen sr-only y not-sr-only. Úsalos; no los redefinas. Si el tuyo no, la implementación de referencia está en references/component-patterns.md.
Lista de verificación de pruebas
Teclado
- Cada elemento interactivo alcanzable con Tab
- El orden de tabulación coincide con el orden visual
- El foco siempre visible
- Escape cierra diálogos y ventanas emergentes
- Las teclas de flecha funcionan en widgets compuestos
- El foco regresa al activador cuando se cierra un diálogo
Lector de pantalla
- Todo el contenido alcanzable
- Propósitos de los controles claros sin contexto circundante
- Etiquetas anunciadas con sus campos
- Errores anunciados mediante
role="alert" - Puntos de referencia presentes
- Elementos decorativos ocultos con
aria-hidden
Color
- 4.5:1 texto normal, 3:1 texto grande y límites de interfaz
- Sin significado solo con color
- Ambos temas verificados
Movimiento
- Movimiento reducido respetado
- Sin parpadeo más allá de tres por segundo
- Contenido de reproducción automática pausable
Automatizado
Los análisis se ejecutan dentro de la suite de Playwright — consulta frontend-testing. El fragmento de Axe en Playwright está en references/component-patterns.md.
Documentos de referencia
| Inmersión profunda | Referencia |
|---|---|
| Implementaciones de componentes accesibles: botón, entrada de formulario, modal, listbox, enlace de salto | references/component-patterns.md |
Skills relacionados
frontend-architecture— capas de componentes y tematizaciónfrontend-testing— donde se ejecutan estas comprobacionesfrontend-component-docs— documentar el contrato de accesibilidad de un componente