Accesibilidad de la interfaz del SGD (WCAG 2.2 AA)
Estándar técnico vigente del proyecto:
accesibilidad/ESTANDAR_ACCESIBILIDAD_SGD_MINJUSDH_v1.1.md(WCAG 2.2 A + AA obligatorio, AAA progresivo). Este documento detalla las reglas por componente y las verificaciones realizadas.
Contraste con el código actual (15/10/2026): consultar el diagnóstico actualizado, §7 y su matriz A/AA/AAA. Las pruebas descritas abajo son históricas. La revisión actual identifica barreras y pruebas pendientes en instancias reales; no acredita conformidad integral. Teclado y alternativa al arrastre con puntero son requisitos distintos: una alternativa de teclado por sí sola no satisface 2.5.7.
Lote 1 implementado tras autorización: ver §8 del diagnóstico. N1/N2/N4/N7/N8 tienen correcciones en las fuentes y verificaciones estáticas/unitarias. Pruebas nuevas de navegador y lector pendientes; no equivale a cierre de conformidad.
Revisión integral del frontend del usuario final y de los componentes compartidos (5 de octubre de 2026). Ejemplo operable en /design-system/componentes (solo desarrollo), sección «Accesibilidad».
Referencias y criterio de conformidad
| Referencia | Qué aporta | Qué no sustituye |
|---|---|---|
| LATAM | Identidad visual: paleta, tipografía, radios, densidad. | Contraste, foco ni teclado. |
| Gob.pe | Claridad de servicio público: lenguaje simple, enlace «Saltar al contenido», foco visible, reducción de movimiento, alto contraste. | Las comprobaciones de WCAG. |
El objetivo normativo es WCAG 2.2 nivel AA (consultado en https://www.w3.org/TR/WCAG22/). Novedades de 2.2 aplicables: 2.4.11 foco no oculto, 2.5.7 arrastre y 2.5.8 tamaño de objetivo (24 px). 4.1.1 Parsing ya no existe. Las referencias de diseño orientan; ninguna se da por cumplida sin comprobación.
Reglas aplicadas por componente
- Lista de opciones común (
.sgd-listbox/.sgd-option): el resaltado de la opción activa y de la elegida se basa en estado (data-highlighted,data-active,data-selected, foco) y no en:hover; la elegida lleva marca (check) además del peso 500, sin depender solo del color. Estados «Buscando…», «Sin resultados.» y error (en rojo, con texto) comoListboxStatus. Axe sin violaciones con la lista abierta en LATAM claro/oscuro e Institucional oscuro (2026-10-08); sin probar con lector de pantalla. - Selectores con búsqueda (
SearchableCombobox): patrón ARIA «combobox con lista» (role="combobox",aria-autocomplete="list",aria-expanded,aria-controls,aria-activedescendant; listarole="listbox"con opcionesrole="option"/aria-selected), nombre por la etiqueta flotante oaria-label, el foco no sale del campo, ↑/↓/Enter/Escape/Tab sin romper la edición de texto (3.2.2, 4.1.2), estados «Buscando…»/«Sin resultados.» comorole="status". Vercomponents/selectores-busqueda.md. - Campos (
FloatingUnderlineInput,MultiSelectCombobox): etiqueta persistente (la flotante o el título externo; el placeholder nunca es el único nombre),requiredcon marca « *» visible, error con texto + icono (role="alert",aria-invalid,aria-describedby; las descripciones propias se suman al error), valores precargados y limpieza con botón de 24 px. El selector con chips no abre su lista al recibir foco (3.2.1): abre con clic, escritura o ↓;aria-controlssolo existe con la lista abierta; su ventana emergente tiene nombre. - Filtros y favoritos (
filter-panel.tsx): el desplegable es undialogcon nombre («Filtros y favoritos»); las opciones sonrole="checkbox"+aria-checkedoperables con Espacio/Enter; las categorías sonCollapsibleconaria-expanded/aria-controls; activo (aria-pressed) y por defecto (texto «Por defecto» en el nombre) se distinguen por separado; abrir el desplegable no marca nada. Cada acción de un favorito (editar, eliminar) es un botón con nombre propio que no aplica ni marca el favorito; aparecen al pasar el mouse o con foco en la fila, y en táctiles en un menú «Más acciones». Tras guardar, eliminar o cancelar una confirmación el foco pasa a un destino lógico (favorito nuevo, siguiente favorito, título «Favoritos», botón «Eliminar»). El panel lateral devuelve el foco al disparador. - Tablas (
DataTable,table*.tsx):th scope="col"con nombre propio (aria-labelledbyal texto visible, sin el valor del separador de ancho),aria-sortcon ciclo asc→desc→asc, encabezados que se ajustan en lugar de truncarse, casillas con nombre por fila y «seleccionar todos» con estadomixed, selección solo por casilla, barra de acciones como región con nombre y recuento cortés (role="status"), buscador conaria-label, conteo de resultados anunciado solo cuando deja de cambiar (700 ms), separador de ancho operable con ← → Inicio (alternativa al arrastre, 2.5.7), región horizontal con nombre y foco solo cuando hay desbordamiento (TableScrollRegion), texto truncado (TruncatedText) alcanzable con Tab y con tooltip solo si realmente se corta. - Diálogos y paneles (
DialogContent,SheetContent): nombre y descripción, foco dentro, Escape según la política (allowed/blocked: las ventanas de sesión siguen sin cerrarse), y devolución del foco al control que las abrió también cuando no hayTrigger(useFocusRestore). - Botones y tooltips: nombre accesible en iconos, tooltip con foco de teclado (
aria-describedby), descartable con Escape; hover plano (no depende de@media (hover)); botón de cierre compartido de 44 px. - Avisos (Sileo): región
aria-live="polite"; cada aviso recibe nombre (aria-label) y su botón «Cerrar notificación». - Navegación: enlace «Saltar al contenido principal» al inicio del shell del funcionario (
#contenido-principal),navcon nombre. - Color y contraste:
--sgd-input-borderclaro ≥ 3:1; error/destructivo corregido en Theme Adobe Spectrum e Institucional (claro#b42318, oscuro#ff9a90con texto oscuro); tonos de etiqueta ≥ 4,5:1 también sobre fila seleccionada y seleccionada con hover. - Movimiento y alto contraste: regla global
prefers-reduced-motion(sin animaciones, transiciones ni scroll suave);forced-colorsda contorno a página actual, pulsado y fila seleccionada. - Objetivo táctil: casillas y «×» de chips con área de 24 px sin cambiar su tamaño visual.
Verificaciones realizadas
Herramientas: axe-core 4.13 (ya presente por eslint-plugin-jsx-a11y, sin dependencia nueva), Chrome instalado controlado por CDP (teclado, ratón, capturas y árbol de accesibilidad reales), ESLint (jsx-a11y) y tsc.
- axe (WCAG 2/2.1/2.2 A y AA + buenas prácticas): 0 violaciones en 18 combinaciones (reposo, desplegable abierto con favorito y categorías expandidas, tabla con selección × 6 temas/modos). Quedan 3 avisos de contraste sobre las etiquetas de la demostración de casillas deshabilitadas (componente inactivo, exento de 1.4.3).
- Teclado real: abrir/cerrar filtros, marcar opción, expandir categoría, Escape con devolución de foco, ordenar, seleccionar filas, ajustar ancho, selector de columnas, guardar/editar/eliminar/cancelar favorito, enlace «Saltar al contenido», diálogos (permitido, bloqueado, confirmación) y panel lateral (focos y Tab dentro del modal).
- Árbol de accesibilidad (nombre, rol y estado calculados por el navegador): encabezados, casillas (
mixed), región de tabla, desplegable, opciones, categorías, combobox, campo obligatorio, interruptor, favorito activo/por defecto y aviso. - Adaptación: viewport de 320 px (400 %) y 720 px (200 %) sin desbordamiento horizontal fuera de las regiones con desplazamiento propio; desplegable y panel lateral utilizables; espaciado de texto 1.4.12 sin recortes salvo las descripciones con
line-clamp(consultables con foco/tooltip); movimiento reducido (duraciones ≈ 0); alto contraste (captura revisada). - Hover real con ratón (cursor de mano y fondo suave) en botón ghost, categorías y opciones.
Limitaciones y pendientes
-
Hallazgos actuales por inspección: N1–N9 y riesgos R1–R4 en el diagnóstico enlazado. Tras el lote 1, los editores de destinatarios/V°B° incluyen teclado y Subir/Bajar; falta reprueba real. El lote 2 agrega ancho numérico y +/- (N3), tabla diaria (N5), contraste oscuro corregido tras medición (N6) y navegación del visor por botones/teclado (N9). Evidencia y límites en §9 del diagnóstico. R1–R4, NVDA y flujos autenticados siguen pendientes.
-
No se probó con lector de pantalla real (NVDA, JAWS, Narrator, VoiceOver): solo árbol de accesibilidad y axe. Probar con NVDA + Firefox/Chrome antes de declarar conformidad.
-
No se verificaron en navegador las pantallas autenticadas (shell del funcionario con enlace «Saltar al contenido», navegación y la bandeja de Emisión): quedan comprobadas por código, ESLint y
tsc. Tampoco las pantallas de/admin: solo los ejemplos del Design System que usan los mismos componentes compartidos. -
Zoom comprobado emulando el ancho del viewport (720 y 320 px), no con zoom real del navegador ni con «solo texto».
-
hover: noneno puede emularse en este Chrome; las reglas planas se verificaron con ratón real. -
2.4.11 (foco no oculto): sin barra fija sobre el contenido en estas pantallas, pero los avisos flotantes pueden cubrir contenido; no se verificó de forma exhaustiva.
-
Las fuentes (Inter e iconos Material Symbols) ya se alojan en el propio SGD (
public/fonts, ver «Fuentes alojadas» endesign-system-sgd.md): sin Google, los iconos no se ven como texto aunque la red bloquee esos dominios. Sigue pendiente, como mejora futura, sustituir la fuente de iconos por SVG incrustados. -
Popovers del administrador sin nombre (selectores de entidad, ciudadano, ubicación, fecha de
admin/) y combos fuera del alcance de esta revisión: pendientes. -
Los tamaños de texto de ayuda de 12 px siguen siendo los del Design System; no se aumentaron.
Cómo repetir las comprobaciones
cd app
node node_modules/eslint/bin/eslint.js src
node scripts/check-icon-font.mjs # iconos usados ⊆ fuente alojada
node node_modules/typescript/bin/tsc --noEmit -p .
axe-core está en app/node_modules/axe-core/axe.min.js: puede inyectarse en la página de pruebas (/design-system/componentes) y ejecutarse con axe.run(document, { runOnly: { type: 'tag', values: ['wcag2a','wcag2aa','wcag21aa','wcag22aa','best-practice'] } }) en los seis temas/modos.
PDF/firma y carga: corrección tras diagnóstico
La revisión del 15-oct-2026 confirmó barreras de lectura textual del PDF, selección del sello en otra página con teclado y controles anidados en Dropzone. El diagnóstico se entregó primero (§10 del informe compartido); tras la aprobación posterior se implementaron las tres correcciones (§11). Ese avance sustituye el estado previo «pendiente de implementar».
- PDF: lectura por página con el visor oficial de PDF.js y formulario «Ubicar sello» con página y coordenadas reales, reutilizando Popover/Input/Button. Sin OCR, sin garantizar orden semántico del original, sin cambios del contrato de firma. Detalle en
components/firma-digital.md. - Dropzone compartido: un único
input type=filenativo sobre la superficie; el contenedor no es botón ni recibe foco. Etiqueta externa medianteid/htmlFor, olabelpara usos independientes; ayuda, estado y error asociados poraria-describedby, error conrole=alert/aria-invalid, obligatoriedad y carga comunicadas. Quitar archivo devuelve el foco al input. Conservar tipos, límites, validaciones y callbacks de cada consumidor; no añadir otro control envolvente. - Reprueba focalizada: lectura de tres páginas (una sin texto), ubicación decimal en página 2, límites, zoom, bloqueo, errores de archivo y foco; cuatro pruebas del helper y muestra en seis apariencias con 0 violaciones axe. Incompletos automáticos y pruebas sin ejecutar quedan en §11; no equivalen a conformidad WCAG del flujo completo.
- Pendiente: NVDA, zoom real, documentos con estructura compleja y recorrido protegido de QA. La muestra del Design System (
#pdf-heading) no firma ni envía archivos y no sustituye esas pruebas.