Hallazgos por componente
Problema
El campo de búsqueda no tiene <label> asociado. El botón lupa es solo un ícono SVG sin aria-label. Los lectores de pantalla anuncian ambos elementos como vacíos o con nombre genérico.
Corrección WCAG 2.2
Usar role="search" en el form, <label> visualmente oculto con clase sr-only, y aria-label explícito en el botón.
Árbol de accesibilidad resultante
Problema
Los segmentos del donut se distinguen únicamente por color (azul=proteínas, naranja=carbohidratos, verde=grasas). Falla para usuarios con daltonismo y cuando el gráfico se renderiza en escala de grises. El elemento <canvas> no tiene alternativa de texto.
Corrección WCAG 2.2
Añadir role="img" + aria-label descriptivo en el canvas, patrones visuales adicionales al color (líneas/puntos), y una tabla de datos como fallback visible.
Problema
El modal de alerta de déficit calórico no atrapa el foco (el usuario puede tabular hasta los elementos debajo), no anuncia su aparición a lectores de pantalla, y no restaura el foco al cerrarse (SC 2.4.3 Focus Order).
Corrección WCAG 2.2
Usar role="alertdialog", aria-modal="true", focus trap con inert en el resto del DOM, botón cerrar con aria-label y restauración de foco al trigger al cerrar.
Árbol de accesibilidad
Problema
Los botones "+", "×" y "✎" miden 18×18 px. WCAG 2.2 SC 2.5.8 (nuevo) exige mínimo 24×24 CSS px. Además carecen de aria-label; el lector de pantalla anuncia "más" (char Unicode) sin contexto.
Corrección WCAG 2.2
Target size de al menos 44×44 px en mobile (best practice) y 24×24 px en web. CSS: padding compensatorio para no alterar el layout.
Problema
El color #888888 sobre #FFFFFF produce un ratio de contraste de 3.54:1. WCAG 2.2 Level AA exige 4.5:1 para texto normal (menos de 18pt).
Texto secundario actual: #888 — ratio 3.54:1 ✗
Texto corregido: #595959 — ratio 7.0:1 ✓
Corrección
Cambiar la paleta de texto secundario a #595959 (ratio 7.0:1) o #767676 (ratio 4.54:1 mínimo). Actualizar el design token en el sistema de diseño.
Verificación de ratios
Mapa de conformidad POUR
P — Perceptible
La información debe presentarse de forma que todos los usuarios puedan percibirla.
- 1.1.1 Non-text Content — Canvas sin texto alternativo
- 1.4.1 Use of Color — Macros solo diferenciados por color
- 1.4.3 Contrast (min) — #888 sobre #fff → 3.54:1 falla
- 1.3.1 Info & Relationships — Estructura HTML semántica OK
O — Operable
Los componentes de la interfaz deben ser operables por cualquier usuario.
- 2.1.2 No Keyboard Trap — Modal sin focus trap controlado
- 2.4.3 Focus Order — Foco no restaurado al cerrar modal
- 2.5.8 Target Size (NEW) — Botones 18×18px < mínimo 24×24px
- 2.1.1 Keyboard — Flujo de tabulación principal funciona
U — Comprensible
La información y el funcionamiento de la interfaz deben ser comprensibles.
- 3.3.7 Redundant Entry (NEW) — Paso 2 repite nombre del alimento innecesariamente
- 3.3.1 Error Identification — Mensajes de error en formularios descriptivos
- 3.3.2 Labels or Instructions — Campos de formulario con instrucciones
R — Robusto
El contenido debe ser interpretable por tecnologías de asistencia actuales y futuras.
- 4.1.2 Name, Role, Value — Botones icono sin aria-label, input sin label
- 4.1.3 Status Messages (NEW) — Modal dinámico sin aria-live ni alertdialog
- 4.1.1 Parsing — HTML válido, sin atributos duplicados
ADR — Decision Record
ADR-ACC-001: Focus Management en modales dinámicos de NutriTrack
ACEPTADOEl modal de alerta calórica aparece dinámicamente cuando el usuario cae por debajo del 30% de su objetivo. Actualmente no atrapa el foco, por lo que los usuarios de teclado/lector de pantalla continúan navegando el fondo de la app sin saber que hay un diálogo activo. Tampoco hay anuncio proactivo de la alerta.
Implementar el patrón ARIA Modal Dialog completo: (1) role="alertdialog" con aria-modal="true", (2) atributo inert en el contenido de fondo mientras el modal está abierto, (3) focus automático al primer elemento interactivo del modal al abrirse, (4) restauración del foco al elemento trigger al cerrarse, y (5) soporte de Escape para cerrar. Esto aplica a todos los modales de la aplicación, no solo al de alerta.
Cumplimiento de SC 2.1.2, 2.4.3 y 4.1.3. Los usuarios de NVDA, JAWS y VoiceOver reciben el anuncio inmediato de la alerta. El atributo inert es nativo HTML y no requiere librerías adicionales (soporte en todos los navegadores modernos desde 2023).
Plan de remediación priorizado
| Prioridad | Componente | SC WCAG 2.2 | Acción | Esfuerzo | Responsable |
|---|---|---|---|---|---|
| P1 | Modal alerta calórica | 2.1.2 · 2.4.3 · 4.1.3 | Añadir role alertdialog + inert + focus restore | Bajo | Frontend dev |
| P1 | Barra de búsqueda | 1.3.1 · 4.1.2 · 2.4.6 | Añadir <label sr-only> + aria-label en botón | Bajo | Frontend dev |
| P1 | Botones de acción | 2.5.8 · 4.1.2 | Padding a 44px hit area + aria-label descriptivo | Bajo | Frontend dev |
| P2 | Gráfico donut macros | 1.1.1 · 1.4.1 | aria-label en canvas + tabla de datos fallback | Medio | Frontend dev + QA |
| P3 | Paleta de texto secundario | 1.4.3 | Cambiar #888 → #595959 en design token | Bajo | Design + Frontend |
| P3 | Formulario multi-paso | 3.3.7 (NEW) | Pre-rellenar nombre alimento en paso 2 desde estado | Medio | Frontend dev |
Anti-patrones detectados en NutriTrack
⚠ Botones vacíos
18 botones de icono sin aria-label en toda la app. El lector anuncia solo el caracter Unicode. Aplicar búsqueda global: button:not([aria-label]).
⚠ Solo color como indicador
Gráficas, badges de estado y leyendas usan únicamente color. Añadir patrón, icono o texto adicional en todos los casos.
⚠ Placeholders como labels
Los campos de formulario usan placeholder como única etiqueta. El placeholder desaparece al escribir, violando SC 3.3.2.
⚠ Contenedores fixed-size
Varios divs tienen height: 300px fijo. Al hacer zoom al 400% el contenido se desborda y queda inaccesible.
⚠ Links no descriptivos
"Ver más" y "Aquí" aparecen 6 veces. Usuarios que navegan por links no saben el destino. Usar texto como "Ver historial completo de comidas".
⚠ Focus indicators eliminados
outline: none en el CSS global suprime el indicador de foco nativo. WCAG 2.2 SC 2.4.11 exige que el foco sea visible. Reemplazar por focus ring personalizado de alto contraste.