Accessibility Specifications
ADEN: Plataforma de Medicina Preventiva Personalizada
Propósito: Especificaciones de accesibilidad para una app de salud donde la mala interpretación de datos puede causar daño real
Impacto: Crítico — afecta seguridad del paciente, cumplimiento regulatorio, y confianza
Estándar: WCAG 2.1 AA mínimo, con aspiración AAA en componentes de datos de salud
1. Objetivo de Accesibilidad
Por Qué una App de Salud Tiene Mayor Responsabilidad
Las apps de salud operan en un nivel de riesgo superior al de apps convencionales:
- Un color malinterpretado puede hacer que un paciente ignore un resultado crítico
- Un número de glucosa sin contexto puede generar pánico innecesario
- Un botón inaccesible puede impedir que alguien agende una cita urgente
- El 36% de adultos tiene health literacy limitada (CDC/NAAL)
- El 12% de hombres y 0.5% de mujeres no distinguen rojo de verde
Estándar Objetivo
| Nivel | Requisito | Aplicación en ADEN |
|---|---|---|
| WCAG 2.1 AA | Mínimo obligatorio | Toda la aplicación |
| WCAG 2.1 AAA | Aspiracional | Datos de salud, resultados de lab, scores |
| ADA/Section 508 | Cumplimiento legal | Toda la aplicación |
Audiencia Prioritaria
| Persona | Edad | Necesidades de Accesibilidad |
|---|---|---|
| Paciente Activado (P4) | 40-55 | Presbicia común, menor contraste visual, posible daltonismo |
| Cuidador Familiar | 35-65 | Puede usar la app en nombre de otro, necesita claridad extrema |
| Adulto Mayor Acompanado | 55-75 | Font scaling, touch targets grandes, lectura simple |
2. Color y Contraste
Ratios Minimos Requeridos
| Elemento | Ratio Mínimo (AA) | Ratio Ideal (AAA) | Norma |
|---|---|---|---|
| Texto normal (<24px) | 4.5:1 | 7:1 | WCAG 1.4.3 / 1.4.6 |
| Texto grande (>=24px o >=19px bold) | 3:1 | 4.5:1 | WCAG 1.4.3 / 1.4.6 |
| Componentes UI (bordes, iconos) | 3:1 | — | WCAG 1.4.11 |
| Focus indicators | 3:1 | — | WCAG 2.4.7 |
Validación de Contraste: Dark Mode
Background principal: #0A0F1E (Deep Navy)
| Elemento | Color | Hex | Ratio vs #0A0F1E |
AA | AAA |
|---|---|---|---|---|---|
| Text Primary | White | #F9FAFB |
18.1:1 | PASS | PASS |
| Text Secondary | Gray Light | #9CA3AF |
6.2:1 | PASS | NO |
| Text Muted | Gray | #6B7280 |
3.9:1 | FAIL texto normal | NO |
| Glow Blue | Blue | #3B82F6 |
4.8:1 | PASS | NO |
| State Óptimo | Emerald | #10B981 |
5.9:1 | PASS | NO |
| State Atención | Red Soft | #F87171 |
5.7:1 | PASS | NO |
| State Revisar | Blue | #3B82F6 |
4.8:1 | PASS | NO |
| Primary CTA | Electric Blue | #0f2fc7 |
2.1:1 | FAIL como texto | — |
Acciones requeridas:
#6B7280(Text Muted): NO usar para texto normal. Solo para labels decorativos de >24px o con acompañamiento de texto legible#0f2fc7sobre#0A0F1E: NO usar como texto. Solo como fondo de botones con texto blanco encima#0f2fc7como fondo con#F9FAFBtexto: ~8.6:1 (PASS AA y AAA)
Validación de Contraste: Light Mode
Background principal: #F5F5F0 (Cream)
| Elemento | Color | Hex | Ratio vs #F5F5F0 |
AA | AAA |
|---|---|---|---|---|---|
| Text Primary | Neutral 900 | #171717 |
15.8:1 | PASS | PASS |
| Text Secondary | Gray 600 | #4B5563 |
7.2:1 | PASS | PASS |
| Text Muted | Gray 400 | #9CA3AF |
2.8:1 | FAIL | FAIL |
| Primary Blue | Blue | #0f2fc7 |
7.4:1 | PASS | PASS |
| State Óptimo | Emerald | #10B981 |
2.9:1 | FAIL como texto | — |
| Error Red | Red | #ef4444 |
3.9:1 | FAIL texto normal | — |
| Card Background | White | #FFFFFF |
1.1:1 | N/A (fondo) | — |
Acciones requeridas:
#9CA3AFsobre cream: NO usar como texto. Buscar alternativa más oscura (#6B7280= 4.5:1, pasa AA)#10B981(Emerald) como texto sobre cream: NO usar solo. Siempre acompañar con icono y label de texto en color que pase#ef4444como texto: Solo para texto grande (>=24px). Para texto normal, usar#DC2626(ratio 5.3:1)- Card white
#FFFFFFsobre cream#F5F5F0: Delimitar cards con borde de 1px mínimo (3:1 ratio)
Regla Crítica: Color No Es Suficiente
WCAG 1.4.1: El color NUNCA puede ser el único medio visual para transmitir información de salud.
Cada estado de salud DEBE tener tres canales simultaneos:
CORRECTO:
[CheckmarkCircle02Icon] En rango óptimo ← Icono + Texto + Color verde
Glucosa: 92 mg/dL
INCORRECTO:
Glucosa: 92 mg/dL ← Solo círculo verde (inaccesible)
| Estado | Color | Icono Requerido | Label de Texto |
|---|---|---|---|
| Óptimo | Emerald #10B981 |
CheckmarkCircle02Icon | “En rango óptimo” |
| Revisar | Blue #3B82F6 |
InformationCircleIcon | “Para revisar” |
| Atención | Red Soft #F87171 |
AlertCircleIcon | “Necesita atención” |
| Pendiente | Gray #9CA3AF |
Clock01Icon | “Pendiente” |
Daltonismo: Alternativas Seguras
ADEN ya usa azul en lugar de verde/rojo para estados, lo cual es correcto. Reglas adicionales:
| Combinación | Seguridad | Uso en ADEN |
|---|---|---|
| Azul + Naranja | Universalmente distinguible | Gráficos comparativos, tendencias |
| Azul + Amarillo | Alta diferenciación | Alertas vs información |
| Rojo + Verde | PROHIBIDO como único diferenciador | Nunca sin icono + texto |
| Formas + Patrones | Siempre seguro | Gráficos de línea (solida vs punteada vs dashed) |
Herramientas de prueba obligatorias:
- Stark (plugin Figma) — Simulación de daltonismo en tiempo real
- Color Oracle (desktop) — Simulación a nivel de sistema operativo
- Sim Daltonism (macOS) — Filtro de pantalla en tiempo real
3. Soporte para Screen Readers
Principios Generales
- Toda imagen necesita
alttext descriptivo y contextualizado - Elementos decorativos:
alt=""yaria-hidden="true" - Orden de lectura debe coincidir con orden visual
- Todos los iconos HugeIcons deben tener
aria-labelo estar marcadosaria-hidden="true"si son decorativos
Patrones ARIA para Datos de Salud
Health Score (Anillo de Progreso)
<div
role="meter"
aria-label="Tu Nivel de Salud"
aria-valuenow="78"
aria-valuemin="0"
aria-valuemax="100"
aria-valuetext="Tu Nivel de Salud: 78 de 100, categoría Bueno"
>
<!-- Visual ring animation -->
<span aria-hidden="true" class="score-ring">78</span>
<!-- Screen reader text -->
<span class="sr-only">Tu Nivel de Salud: 78 de 100, categoría Bueno</span>
</div>
Resultado de Biomarcador
<div role="group" aria-label="Resultado de Glucosa">
<span class="sr-only">
Glucosa: 92 miligramos por decilitro, en rango óptimo.
Tu tendencia de los últimos 90 días se ve bien.
</span>
<div aria-hidden="true">
<!-- Visual presentation -->
<span class="metric-value">92</span>
<span class="metric-unit">mg/dL</span>
<span class="metric-badge badge-optimal">En rango óptimo</span>
</div>
</div>
Resultado Fuera de Rango
<div role="group" aria-label="Resultado de Glucosa que necesita atención">
<span class="sr-only">
Glucosa: 142 miligramos por decilitro. Tu nivel necesita atención.
Tu equipo de salud ya fue notificado.
Te recomendamos agendar una cita.
</span>
<!-- Visual presentation with aria-hidden="true" -->
</div>
Gráficos y Visualizaciones
Cada gráfico DEBE tener un resumen de texto debajo:
<figure role="img" aria-label="Tendencia de glucosa en los últimos 90 días">
<div aria-hidden="true">
<!-- Chart SVG/Canvas -->
</div>
<figcaption>
<p class="chart-summary">
Tu glucosa se mantuvo estable entre 85 y 95 mg/dL en los últimos 90 días.
La tendencia es positiva comparada con tu ciclo anterior.
</p>
<!-- Optional: data table for screen readers -->
<details>
<summary>Ver datos en tabla</summary>
<table>
<caption>Lecturas de glucosa, últimos 90 días</caption>
<!-- Table data -->
</table>
</details>
</figcaption>
</figure>
Alertas y Notificaciones
<!-- Alerta NO crítica (resultados listos, recordatorios) -->
<div
role="status"
aria-live="polite"
aria-atomic="true"
>
Tus exámenes estan listos. Veamos como te fue.
</div>
<!-- Alerta CRITICA (resultado que necesita atención, error de sistema) -->
<div
role="alert"
aria-live="assertive"
aria-atomic="true"
>
Tu nivel de glucosa necesita atención. Tu equipo de salud fue notificado.
</div>
<!-- Notificación de estado (guardado, sincronizado) -->
<div
role="status"
aria-live="polite"
>
Check-in completo. Llevas 23 días consecutivos.
</div>
Errores en Formularios
<div class="form-field">
<label for="email-input" id="email-label">
Correo electrónico
<span aria-hidden="true" class="required-indicator">*</span>
</label>
<input
id="email-input"
type="email"
aria-required="true"
aria-invalid="true"
aria-describedby="email-error email-hint"
aria-labelledby="email-label"
/>
<span id="email-hint" class="input-hint">
Ejemplo: tu@correo.com
</span>
<span id="email-error" role="alert" class="error-message">
El correo no tiene un formato válido. Revisa que tenga @ y un dominio.
</span>
</div>
Modales
<div
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-description"
>
<h2 id="modal-title">Resultado de exámenes</h2>
<p id="modal-description">
Tus exámenes del 15 de marzo estan listos para revisar.
</p>
<!-- Content -->
<button aria-label="Cerrar dialogo">
<Cancel01Icon aria-hidden="true" />
</button>
</div>
Comportamiento del modal:
- Focus trap: Tab cicla dentro del modal, no sale
- Al abrir: focus va al primer elemento interactivo (o al título)
- Al cerrar: focus regresa al elemento que abrió el modal
- Escape cierra el modal
- Click fuera del modal lo cierra (con backdrop)
aria-hidden="true"en todo el contenido detrás del modal
4. Navegación por Teclado
Requisitos Generales
| Tecla | Acción | Contexto |
|---|---|---|
| Tab | Siguiente elemento interactivo | Global |
| Shift + Tab | Elemento interactivo anterior | Global |
| Enter | Activar botón o link | Botones, links |
| Space | Activar botón, toggle checkbox | Botones, checkboxes |
| Escape | Cerrar modal, drawer, dropdown | Overlays |
| Arrow Up/Down | Navegar opciones | Selects, menus, radio groups |
| Arrow Left/Right | Cambiar tab, mover slider | Tabs, date pickers, sliders |
| Home | Ir al primer item | Listas, tabs |
| End | Ir al último item | Listas, tabs |
Skip-to-Content Link
<body>
<a href="#main-content" class="skip-link">
Saltar al contenido principal
</a>
<!-- Navigation -->
<main id="main-content" tabindex="-1">
<!-- Page content -->
</main>
</body>
.skip-link {
position: absolute;
top: -40px;
left: 0;
z-index: 100;
padding: 8px 16px;
background: #0f2fc7;
color: #F9FAFB;
font-size: 16px;
text-decoration: none;
transition: top 150ms ease;
}
.skip-link:focus {
top: 0;
}
Tab Order
El orden de tabulación debe seguir el flujo visual:
- Skip-to-content link
- Header / navegación principal
- Contenido hero (score principal, alerta crítica)
- Cards de acción (izquierda a derecha, arriba a abajo)
- Contenido secundario
- Footer / navegación secundaria
Nunca usar tabindex mayor a 0. Solo tabindex="0" (orden natural) o tabindex="-1" (focusable por script, no por tab).
Focus Indicators
/* Focus visible para todos los elementos interactivos */
:focus-visible {
outline: 2px solid #3B82F6;
outline-offset: 2px;
border-radius: 4px;
}
/* Dark mode: outline azul claro sobre fondo oscuro */
[data-theme="dark"] :focus-visible {
outline-color: #60A5FA;
}
/* Light mode: outline azul oscuro sobre fondo claro */
[data-theme="light"] :focus-visible {
outline-color: #0f2fc7;
}
/* NUNCA eliminar outline sin reemplazo */
/* PROHIBIDO: :focus { outline: none; } */
El indicador de focus debe:
- Ser visible en ambos modos (dark y light)
- Tener ratio de contraste 3:1 mínimo contra el fondo
- No depender solo de color (el outline es un cambio de forma)
- Tener
outline-offsetde al menos 2px para no confundirse con el borde del elemento
5. Touch Targets
Tamanos Minimos
| Estándar | Tamaño Mínimo | Aplicación |
|---|---|---|
| WCAG 2.5.5 (AAA) | 44x44px | Objetivo para ADEN |
| Material Design | 48x48dp | Objetivo ideal |
| Apple HIG | 44x44pt | iOS nativo |
Reglas de Implementación
/* Tamano mínimo para todos los elementos interactivos */
button,
a,
input[type="checkbox"],
input[type="radio"],
.interactive {
min-width: 44px;
min-height: 44px;
}
/* Spacing entre targets tocables */
.action-group > * + * {
margin-left: 8px; /* Mínimo 8px entre targets */
}
Spacing Entre Targets
| Contexto | Spacing Mínimo | Razón |
|---|---|---|
| Botones adyacentes | 8px | Prevenir toque accidental |
| Items de lista | 8px vertical | Facilitar selección |
| Iconos de acción en toolbar | 12px | Mayor precisión requerida |
| Links en texto corrido | 4px padding extra | Touch target expansión |
Zona del Pulgar
En dispositivos moviles, las acciones primarias deben estar en la zona de alcance comodo del pulgar:
Zona difícil ← Información, navegación
Zona media ← Contenido, datos
Zona comoda ← CTAs, navegación tab, check-in
Aplicación en ADEN:
- Botón de check-in diario: zona comoda inferior
- Tab navigation: zona comoda inferior
- Score/métricas: zona media (lectura, no acción)
- Settings/perfil: zona superior (acceso infrecuente)
Gestos con Alternativa
Todo gesto de swipe DEBE tener un botón alternativo visible:
| Gesto | Alternativa Requerida |
|---|---|
| Swipe para descartar alerta | Botón “X” o “Descartar” visible |
| Swipe entre tabs | Tabs tocables visibles arriba |
| Pull-to-refresh | Botón “Actualizar” visible |
| Pinch-to-zoom en gráficos | Botones “+” / “-” visibles |
6. Health Literacy
Nivel de Lectura Objetivo
| Fuente | Recomendación |
|---|---|
| AMA | Grado 6 máximo |
| CDC | Grado 3-5 |
| ADEN objetivo | Grado 6 máximo (español colombiano) |
8 Reglas de Comunicación Clara
- Una idea por oración — máximo 15-20 palabras
- Voz activa siempre — “Toma tu suplemento” no “El suplemento debe ser tomado”
- Palabras cotidianas — “Tus exámenes” no “Biomarcadores”
- Listas cortas — 3 a 7 items máximo por lista
- Números claros — “3 de cada 10” no “30%”
- Verbos de acción — “Haz”, “Toma”, “Ve”, no “Considere”
- Sin doble negativo — “Es seguro” no “No es inseguro”
- Contexto antes de dato — Explicar que mide ANTES de dar el número
Glosario de Terminos Médicos
Disponible en toda la app vía tooltip o icono de información:
| Termino Médico | Explicación en ADEN |
|---|---|
| Biomarcadores | Tus exámenes |
| Perfil lipídico | Niveles de colesterol |
| HbA1c | Control de azucar a largo plazo |
| HOMA-IR | Resistencia a insulina |
| Health Score | Tu Nivel de Salud |
| Adherencia | Tu constancia / Tu progreso |
| Protocolo | Tu plan personalizado |
| Baseline | Tu punto de partida |
Implementación del glosario:
<!-- Tooltip contextual para terminos médicos -->
<span class="medical-term">
<span>Tu control de azucar a largo plazo</span>
<button
aria-label="Más información sobre control de azúcar a largo plazo"
aria-expanded="false"
aria-controls="tooltip-hba1c"
>
<InformationCircleIcon aria-hidden="true" />
</button>
<div
id="tooltip-hba1c"
role="tooltip"
hidden
>
El HbA1c mide tu nivel promedio de azucar en sangre
durante los últimos 2-3 meses. Es como una foto de
como ha estado tu azucar, no solo hoy sino en general.
</div>
</span>
Comunicación de Resultados de Salud
Todo resultado debe seguir este patrón:
1. CONTEXTO → Que se midio (en palabras simples)
2. RESULTADO → El número con su unidad
3. ESTADO → Que significa para ti (icono + color + texto)
4. ACCION → Que puedes hacer
Ejemplo completo accesible:
<article aria-label="Resultado de glucosa en ayunas">
<h3>Tu azucar en ayunas</h3>
<p class="result-value" aria-hidden="true">92 mg/dL</p>
<p class="sr-only">92 miligramos por decilitro</p>
<div class="result-status" role="status">
<CheckmarkCircle02Icon aria-hidden="true" />
<span>En rango óptimo</span>
</div>
<p class="result-context">
Tu tendencia de los últimos 90 días se ve bien.
</p>
<a href="/paciente/exámenes/glucosa">
Ver historial completo
</a>
</article>
7. Movimiento y Sensibilidad Vestibular
Respetar prefers-reduced-motion
/* Base: animaciones normales */
.score-ring {
animation: ring-fill 1.2s ease-out forwards;
}
.fade-in-up {
animation: fadeInUp 260ms cubic-bezier(0.4, 0, 0.2, 1);
}
.skeleton-shimmer {
animation: shimmer 500ms infinite;
}
/* Reduced motion: saltar al estado final */
@media (prefers-reduced-motion: reduce) {
.score-ring {
animation: none;
/* Mostrar estado final directamente */
}
.fade-in-up {
animation: none;
opacity: 1;
transform: none;
}
.skeleton-shimmer {
animation: none;
/* Mostrar placeholder estatico */
}
/* Transiciones suaves reducidas a instantaneas */
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Reglas de Movimiento
| Elemento | Comportamiento Normal | Comportamiento Reduced Motion |
|---|---|---|
| Score ring | Animación de llenado 1.2s | Mostrar valor final inmediatamente |
| FadeInUp cards | Delay escalonado 0.2s | Todas visibles inmediatamente |
| Skeleton shimmer | Pulso infinito | Fondo estatico gris |
| Modal enter | Slide-up 200ms | Aparición instantanea |
| Button hover | TranslateY 150ms | Cambio de color instantaneo |
| Gráficos de tendencia | Dibujo progresivo de línea | Línea completa instantanea |
| Celebration confetti | Particulas animadas | Badge estatico “Completado” |
Prohibiciones Absolutas
- NO autoplay de video — Siempre requiere acción del usuario
- NO parallax scrolling — En ninguna sección
- NO animaciones de fondo continuas — Solo animaciones disparadas por acción
- NO parpadeo o flash — Cero elementos que parpadeen >3 veces por segundo (WCAG 2.3.1)
- NO carruseles automaticos — Solo avance manual
8. Tipografia para Accesibilidad
Tamanos Minimos
| Elemento | Tamaño Mínimo | Peso | Line Height |
|---|---|---|---|
| Body text | 16px | 400 | 1.5 (24px) |
| Labels / captions | 14px | 500 | 1.4 (20px) |
| Metric values | 24px+ | 700 | 1.2 |
| Headlines (h1) | 28px+ | 700 | 1.3 |
| Headlines (h2) | 22px+ | 600 | 1.3 |
| Button text | 16px | 500-600 | 1.0 |
| Error messages | 14px | 500 | 1.4 |
| Tooltip text | 14px | 400 | 1.4 |
Font Scaling
/* NUNCA fijar font-size en px a nivel de root */
/* CORRECTO: usar rem que respeta configuración del usuario */
html {
font-size: 100%; /* Respeta setting del sistema */
}
body {
font-size: 1rem; /* 16px default, escala con usuario */
line-height: 1.5;
}
h1 { font-size: 1.75rem; } /* 28px default */
h2 { font-size: 1.375rem; } /* 22px default */
/* Métricas de salud: ligeramente más grandes */
.metric-value {
font-size: 1.5rem; /* 24px default */
font-weight: 700;
font-variant-numeric: tabular-nums; /* Alineacion de números */
}
Reglas críticas:
- Soportar font scaling hasta 200% sin perdida de contenido ni overlap
- NO usar
max-heightconoverflow: hiddenen contenedores de texto - Los contenedores deben expandirse con el contenido
- Números de métricas DEBEN usar
font-variant-numeric: tabular-numspara alineación correcta - Peso tipografico: suficiente contraste entre niveles de jerarquía (ej: 400 body vs 700 heading, nunca 400 vs 500 en elementos adyacentes)
Espaciado de Texto
| Propiedad | Valor Mínimo | Norma |
|---|---|---|
| Line height | 1.5x font size | WCAG 1.4.12 |
| Paragraph spacing | 2x font size | WCAG 1.4.12 |
| Letter spacing | 0.12x font size | WCAG 1.4.12 |
| Word spacing | 0.16x font size | WCAG 1.4.12 |
9. Formularios e Inputs
Labels Visibles
<!-- CORRECTO: Label visible siempre -->
<label for="weight-input">Tu peso actual (kg)</label>
<input
id="weight-input"
type="number"
inputmode="decimal"
aria-required="true"
autocomplete="off"
placeholder="Ej: 72.5"
/>
<!-- INCORRECTO: Solo placeholder como label -->
<input placeholder="Peso" />
Regla: El placeholder NUNCA reemplaza al label. Es solo una pista adicional que desaparece al escribir.
Mensajes de Error: Específicos y Accionables
| Tipo de Error | Mensaje Incorrecto | Mensaje Correcto |
|---|---|---|
| Campo vacio | “Campo requerido” | “Escribe tu correo para continuar” |
| Formato inválido | “Formato inválido” | “El correo debe tener @ y un dominio (ej: tu@correo.com)” |
| Fuera de rango | “Valor inválido” | “El peso debe estar entre 30 y 300 kg” |
| Contraseña debil | “Contraseña no válida” | “Tu contraseña necesita al menos 8 caracteres y un número” |
Atributos Requeridos
<input
id="field-id"
type="text"
aria-required="true" <!-- Campo obligatorio -->
aria-invalid="false" <!-- Cambiar a true si hay error -->
aria-describedby="field-error field-hint" <!-- Vincula hint y error -->
aria-labelledby="field-label" <!-- Vincula label externo -->
autocomplete="email" <!-- Autocompletado semantico -->
/>
Autocomplete para Campos Estándar
| Campo | Atributo autocomplete |
|---|---|
| Nombre completo | name |
email |
|
| Teléfono | tel |
| Fecha de nacimiento | bday |
| Dirección | street-address |
| Ciudad | address-level2 |
| Código postal | postal-code |
Input Masks
Cuando un campo requiere formato específico:
<label for="phone-input">Tu celular</label>
<input
id="phone-input"
type="tel"
inputmode="numeric"
autocomplete="tel"
aria-describedby="phone-format"
placeholder="300 123 4567"
/>
<span id="phone-format" class="input-hint">
Formato: 10 digitos sin indicativo de pais
</span>
10. Checklist de Testing
Testing Manual
- [ ] Screen reader (VoiceOver iOS): Navegar toda la app solo con VoiceOver. Verificar que cada pantalla se lea de forma lógica y completa
- [ ] Screen reader (TalkBack Android): Misma prueba en Android. Verificar anuncios de roles, estados, y valores
- [ ] Navegación solo con teclado: Completar un flujo crítico (onboarding, check-in, ver resultados) sin mouse. Verificar que el focus sea siempre visible y lógico
- [ ] Auditoria de contraste de color (Dark Mode): Verificar todos los pares de color texto/fondo contra ratios AA mínimo
- [ ] Auditoria de contraste de color (Light Mode): Misma verificación para light mode
- [ ] Sim Daltonism — Deuteranopia: Verificar que ningun estado de salud dependa solo de rojo/verde
- [ ] Sim Daltonism — Protanopia: Verificar gráficos y badges en modo protanopia
- [ ] Sim Daltonism — Tritanopia: Verificar pares azul/amarillo
- [ ] Font scaling 200%: Escalar fuentes al 200% en iOS y Android. Verificar que no haya overflow, truncado, ni overlap
- [ ] Reduced motion: Activar “Reducir movimiento” en sistema. Verificar que score rings, skeletons, y fade-ins se resuelvan instantaneamente
- [ ] Auditoria de touch targets: Medir todos los botones, links, checkboxes. Ninguno menor a 44x44px
- [ ] Revisión de health literacy: Enviar copys de resultados de salud a test de legibilidad. Verificar grado 6 máximo
- [ ] Prueba con usuario real (40-55 años): Persona del segmento Paciente Activado completa flujo crítico sin asistencia
Testing Automatizado
| Herramienta | Que Detecta | Cuando Ejecutar |
|---|---|---|
| axe-core | Violaciones WCAG en DOM | CI/CD en cada PR |
| Lighthouse Accessibility | Score de accesibilidad, issues críticos | CI/CD en cada PR |
| eslint-plugin-jsx-a11y | Problemas de accesibilidad en JSX/TSX | Linting en desarrollo |
| Pa11y | WCAG 2.1 AA compliance automatizada | Nightly en staging |
| Storybook a11y addon | Testing de componentes individuales | Desarrollo de componentes |
Configuración de axe-core
// cypress/support/commands.js o playwright config
import 'cypress-axe';
// En cada test crítico:
cy.checkA11y(null, {
rules: {
'color-contrast': { enabled: true },
'image-alt': { enabled: true },
'label': { enabled: true },
'aria-roles': { enabled: true },
'aria-required-attr': { enabled: true },
},
// ADEN: reglas adicionales para health apps
tags: ['wcag2a', 'wcag2aa', 'wcag21aa'],
});
Frecuencia de Testing
| Test | Frecuencia | Responsable |
|---|---|---|
| axe-core automatizado | Cada PR | CI/CD |
| Lighthouse audit | Semanal | Desarrollo |
| Screen reader manual | Cada release | QA |
| Daltonismo visual | Cada nuevo componente | Diseño |
| Font scaling | Cada release | QA |
| Health literacy review | Cada nuevo copy de salud | Contenido |
| User testing (45+) | Cada ciclo mayor | UX Research |
Resumen de Implementación
Prioridad 1 (Bloquea lanzamiento)
- Contraste de color AA en todos los textos (ambos modos)
- Color nunca como único indicador de estado de salud
- Screen reader labels en todos los resultados de salud
- Keyboard navigation funcional en flujos críticos
- Touch targets mínimos de 44x44px
- Skip-to-content link
- Focus indicators visibles
- Errores de formulario vinculados con
aria-describedby
Prioridad 2 (Primer mes post-lanzamiento)
prefers-reduced-motionimplementado en todas las animaciones- Glosario de terminos médicos accesible desde cada pantalla
- Resumenes de texto debajo de cada gráfico
- Font scaling hasta 200% sin rotura de layout
- axe-core integrado en CI/CD
Prioridad 3 (Mejora continua)
- Aspiración AAA en contraste para datos de salud
- User testing con personas de 40-55 años
- Pa11y nightly en staging
- Storybook a11y addon para libreria de componentes
- Documentación de patrones ARIA para nuevos componentes
Anterior: states – Estados globales y manejo de errores
Siguiente: edge-cases – Situaciones especiales y validaciones