CULTIVA IA — Auditoría de Accesibilidad

Pruebas con Lectores de Pantalla
FlorHub SaaS

Panel de gestión de pedidos B2B — React SPA — Mercado ES/LATAM

Fecha
Junio 2026
Versión
v2.4.1
Estándar objetivo
WCAG 2.1 AA
Pantallas probadas
5
Issues encontrados
11
Estado
⚠ Requiere correcciones
🔊
Score global
62%
criterios WCAG AA
🔴
Críticos
4
bloquean el uso
🟡
Medios
5
dificultan el uso
🔵
Menores
2
mejora recomendada
Superados
18
criterios OK
🖥️
Lectores de Pantalla Evaluados
NVDA + Firefox (Windows) — Prioridad ALTA VoiceOver + Safari (macOS) — Prioridad ALTA VoiceOver + Safari (iOS) — Prioridad MEDIA TalkBack + Chrome (Android) — Prioridad MEDIA JAWS + Chrome (Windows) — Prioridad BAJA
Dashboard de Pedidos
Issues críticos2
Issues medios2
Criterios OK5
Modal Confirmación
Issues críticos2
Issues medios1
Criterios OK3
Formulario Multi-paso
Issues críticos0
Issues medios2
Criterios OK4
Notificaciones Live
Issues críticos0
Issues medios0
Issues menores2
Pestañas (Catálogo / Historial / Analítica)
Issues críticos0
Issues medios0
Criterios OK6
🐛
Issues Encontrados por Lector de Pantalla
Severidad Componente / Descripción Lectores afectados WCAG
🔴 Crítico
Modal de Confirmación — Sin trampa de foco
Al abrir el modal de confirmación de pedido, el foco no queda atrapado dentro. Con Tab el usuario puede salir del modal y seguir navegando el contenido de fondo, perdiendo contexto.
NVDAVOJAWS
2.1.2 Sin trampa de teclado
🔴 Crítico
Tabla de Inventario — Encabezados sin scope
Las celdas <th> de la tabla de inventario no tienen atributo scope ni id asociado. NVDA y JAWS no anuncian el encabezado al navegar por celda con Ctrl+Alt+Flechas.
NVDAJAWS
1.3.1 Info y relaciones
🔴 Crítico
Botones de acción — Solo icono SVG, sin label
Los botones "Editar pedido", "Eliminar" y "Ver detalle" contienen únicamente SVG sin aria-label ni texto alternativo. Todos los lectores anuncian solo "botón" sin propósito.
NVDAVOJAWSTB
4.1.2 Nombre, rol, valor
🔴 Crítico
Modal — No se anuncia como diálogo al abrir
El contenedor del modal usa <div class="modal"> sin role="dialog" ni aria-modal="true". Los lectores no anuncian que se ha abierto un diálogo y no saben que el contenido de fondo está bloqueado.
NVDAVOJAWS
4.1.2 Nombre, rol, valor
🟡 Medio
Formulario — Errores no vinculados al campo
Los mensajes de error de validación están visualmente próximos al campo pero sin aria-describedby ni aria-invalid. VoiceOver y NVDA no los anuncian al recibir el foco el campo.
NVDAVO
3.3.1 Identificación de errores
🟡 Medio
Formulario Multi-paso — Pasos sin anuncio de progreso
Al avanzar de paso (1/3 → 2/3 → 3/3) el lector no anuncia en qué paso se encuentra el usuario. Falta un live region o un h2 enfocable que informe del contexto actual.
NVDAVOTB
3.2.3 Navegación consistente
🟡 Medio
Filtros de tabla — Combo sin aria-expanded
Los desplegables de filtro ("Estado", "Fecha", "Distribuidor") no actualizan aria-expanded al abrirse/cerrarse. NVDA no anuncia si el menú está abierto o cerrado.
NVDAJAWS
4.1.2 Nombre, rol, valor
🟡 Medio
Dashboard — Sin landmark main
La página carece de landmark <main> o role="main". Los usuarios no pueden saltar al contenido principal con la tecla D (NVDA) o el rotor (VoiceOver).
NVDAVO
1.3.6 Identificar el propósito
🟡 Medio
Modal — Foco no regresa al disparador al cerrar
Al cerrar el modal (botón Cancelar o Escape), el foco va al inicio del documento en vez de volver al botón que abrió el modal. Desorientador para usuarios de teclado y lector.
NVDAVOJAWS
2.4.3 Orden de foco
🔵 Bajo
Notificaciones — aria-live="assertive" demasiado agresivo
Las notificaciones de actualización de estado del pedido interrumpen la lectura en curso. Deberían usar aria-live="polite" salvo para alertas críticas.
NVDAVO
Buenas prácticas ARIA
🔵 Bajo
Links de paginación sin contexto
Los links de paginación dicen "Siguiente" y "Anterior" sin contexto de página. Mejor: "Página siguiente (3 de 8)" con aria-label.
NVDAVOJAWS
2.4.6 Encabezados y etiquetas
✅ OK
Pestañas Catálogo/Historial/Analítica
Implementación correcta de role="tablist", role="tab" y role="tabpanel" con navegación de flechas. Todos los lectores anuncian correctamente el tab activo y el panel asociado.
NVDAVOJAWSTB
ARIA 1.1 — Tabs Pattern
🔧
Correcciones Prioritarias con Código

1. Botones con solo icono SVG → Añadir aria-label

Antes (incorrecto)
<button class="action-btn"> <svg width="20" height="20">...</svg> </button> <!-- Anuncia: "botón" — inútil -->
Después (correcto)
<button class="action-btn" aria-label="Editar pedido #FH-2241"> <svg aria-hidden="true" width="20" height="20">...</svg> </button> <!-- Anuncia: "Editar pedido #FH-2241, botón" -->

2. Modal accesible con trampa de foco y anuncio de diálogo

Antes (incorrecto)
<div class="modal"> <h2>Confirmar pedido</h2> <button>Cancelar</button> <button>Confirmar</button> </div>
Después (correcto)
<div role="dialog" aria-modal="true" aria-labelledby="modal-title" aria-describedby="modal-desc" > <h2 id="modal-title">Confirmar pedido #FH-2241</h2> <p id="modal-desc">Se enviará el pedido de 200 rosas. Esta acción no se puede deshacer.</p> <button id="btn-cancel">Cancelar</button> <button id="btn-confirm">Confirmar pedido</button> </div> // JS — Trampa de foco + retorno al disparador function openModal(modal, trigger) { lastFocus = trigger; modal.querySelector('h2').focus(); modal.addEventListener('keydown', trapFocus); } function closeModal(modal) { modal.removeEventListener('keydown', trapFocus); lastFocus.focus(); // Retorna al botón disparador } function trapFocus(e) { const focusable = modal.querySelectorAll( 'button, [href], input, [tabindex]:not([tabindex="-1"])' ); const first = focusable[0], last = focusable[focusable.length - 1]; if (e.key === 'Tab') { if (e.shiftKey && document.activeElement === first) { last.focus(); e.preventDefault(); } else if (!e.shiftKey && document.activeElement === last) { first.focus(); e.preventDefault(); } } if (e.key === 'Escape') closeModal(modal); }

3. Errores de formulario vinculados al campo

Antes (incorrecto)
<input type="email" name="email" /> <span class="error">Email no válido</span>
Después (correcto)
<label for="email">Email de contacto</label> <input id="email" type="email" name="email" aria-invalid="true" aria-describedby="email-error" /> <span id="email-error" role="alert"> Email no válido. Ej: pedidos@floreria.es </span> <!-- NVDA/VO anuncian el error al recibir foco el campo -->

4. Encabezados de tabla con scope

Antes
<th>Referencia</th><th>Flor</th><th>Stock</th>
Después
<th scope="col">Referencia</th> <th scope="col">Variedad</th> <th scope="col">Stock disponible</th> <!-- NVDA anuncia: "Referencia, Variedad, columna 2" al navegar -->
⌨️
Comandos Clave — NVDA + Firefox
AcciónTecla
Siguiente encabezado
H
Leer todo desde cursor
Ins+
Siguiente campo de formulario
F
Siguiente botón
B
Siguiente landmark
D
Lista de elementos
Ins+F7
Navegar celdas de tabla
Ctrl+Alt+←→↑↓
Cambiar modo Browse/Focus
Ins+Space
🍎
Comandos Clave — VoiceOver (macOS)
AcciónTecla (VO = Ctrl+Opt)
Activar VoiceOver
Cmd+F5
Leer todo
VO+A
Abrir Rotor
VO+U
Siguiente encabezado
VO+Cmd+H
Siguiente enlace
VO+Cmd+L
Activar elemento
VO+Space
Entrar en grupo
VO+Shift+
Parar lectura
Ctrl
📋
Checklist de Regresión — Próxima Versión (v2.5.0)
  • Modal: añadir role="dialog", aria-modal, aria-labelledby, trampa de foco y retorno al disparador Responsable: Equipo Frontend — Estimación: 2h — Prioridad: P0
  • Botones de acción: añadir aria-label con contexto de fila (ej. "Editar pedido #FH-2241") Responsable: Equipo Frontend — Estimación: 1h — Prioridad: P0
  • Tabla inventario: añadir scope="col" a todos los <th> y caption descriptivo Responsable: Equipo Frontend — Estimación: 1h — Prioridad: P0
  • Formulario: vincular errores con aria-describedby + aria-invalid en campos inválidos Responsable: Equipo Frontend — Estimación: 2h — Prioridad: P1
  • Layout: envolver contenido principal en <main> y añadir landmarks nav, header, footer Responsable: Equipo Frontend — Estimación: 1h — Prioridad: P1
  • Formulario multi-paso: anunciar número de paso con live region al avanzar Responsable: Equipo Frontend — Estimación: 2h — Prioridad: P1
  • Filtros desplegables: actualizar aria-expanded dinámicamente Responsable: Equipo Frontend — Estimación: 1h — Prioridad: P1
  • !
    Notificaciones: cambiar assertive → polite para updates de estado no urgentes Responsable: Equipo Frontend — Estimación: 30min — Prioridad: P2
  • !
    Paginación: añadir aria-label con contexto de página ("Página siguiente, 3 de 8") Responsable: Equipo Frontend — Estimación: 30min — Prioridad: P2
  • Pestañas: implementación ARIA correcta con tablist, tab, tabpanel Verificado con NVDA, VoiceOver, JAWS y TalkBack — Sin issues
  • Orden de tabulación lógico en todos los formularios Tab order corresponde al orden visual — Sin issues
  • Contraste de color — ratio mínimo 4.5:1 para texto normal Verificado con herramienta Color Contrast Checker — Aprobado