WCAG 2.2 Accesibilidad — Guía para Desarrolladores
Guía práctica de cumplimiento WCAG 2.2: perceptible, operable, comprensible y confiable, con ejemplos de código para desarrollo web accesible.
Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.
Visión General
La accesibilidad web (a11y) asegura que las personas con discapacidades puedan percibir, entender, navegar e interactuar con el contenido web. WCAG 2.2 (Pautas de Accesibilidad para Contenido Web) es el estándar actual, organizado en torno a cuatro principios: Perceptible, Operable, Comprensible y Confiable (POUR). A continuación: los criterios de éxito más impactantes con ejemplos de código prácticos.
Cuándo Usar
-
For alternatives, see Complete Guide to Web Accessibility: WCAG 2.2 Compliance.
-
Estás construyendo sitios o aplicaciones web de acceso público
-
Necesitas cumplir requisitos legales (ADA, EAA, Sección 508)
-
Quieres mejorar la usabilidad para todos los usuarios, incluidos quienes usan lectores de pantalla o navegación por teclado
-
Estás realizando una auditoría de accesibilidad
Los Cuatro Principios (POUR)
| Principio | Qué Significa | Criterios Clave |
|---|---|---|
| Perceptible | La información debe presentarse de formas que los usuarios puedan percibir | Texto alternativo, contraste de color, texto redimensionable |
| Operable | Los componentes de interfaz deben ser operables por todos los usuarios | Navegación por teclado, indicadores de foco, tiempo |
| Comprensible | La información y operación deben ser comprensibles | Texto legible, comportamiento predecible, prevención de errores |
| Confiable | El contenido debe funcionar con tecnologías de asistencia actuales y futuras | HTML válido, roles ARIA, nombre-rol-valor |
Perceptible
Alternativas de Texto (1.1.1)
Todo contenido no textual debe tener una alternativa de texto.
<!-- Bien: texto alt descriptivo -->
<img src="chart.png" alt="Gráfico de barras mostrando crecimiento de ingresos de Q1 a Q4 de 2M a 5M" />
<!-- Bien: imagen decorativa oculta a lectores de pantalla -->
<img src="decoration.png" alt="" />
<!-- Mal: alt faltante o inútil -->
<img src="chart.png" />
<img src="chart.png" alt="imagen" />
Contraste de Color (1.4.3)
El texto debe tener contraste suficiente contra su fondo.
| Nivel | Texto Normal | Texto Grande |
|---|---|---|
| AA | 4.5:1 | 3:1 |
| AAA | 7:1 | 4.5:1 |
/* Verificar con herramientas como WebAIM Contrast Checker */
.text-primary {
color: #1a1a1a; /* gris oscuro */
background: #ffffff;
/* Ratio: 16.1:1 — pasa AAA */
}
.text-muted {
color: #767676; /* gris medio */
background: #ffffff;
/* Ratio: 4.6:1 — pasa AA, no AAA */
}
Texto Redimensionable (1.4.4)
El texto debe ser redimensionable hasta 200% sin pérdida de contenido o funcionalidad.
/* Bien: unidades relativas */
body {
font-size: 100%; /* respeta la configuración del navegador del usuario */
}
h1 {
font-size: 2rem; /* escala con el tamaño de fuente raíz */
}
/* Mal: píxeles fijos que rompen el zoom */
body { font-size: 16px; }
Operable
Accesible por Teclado (2.1.1)
Toda la funcionalidad debe estar disponible desde el teclado.
<!-- Bien: elementos nativos son accesibles por teclado -->
<button onclick="submit()">Enviar</button>
<a href="/next">Siguiente Página</a>
<!-- Mal: div fingiendo ser un botón -->
<div class="btn" onclick="submit()">Enviar</div>
<!-- Bien: componente personalizado con soporte de teclado -->
<div role="button" tabindex="0"
onclick="submit()"
onkeydown="if(event.key==='Enter') submit()">
Enviar
</div>
Foco Visible (2.4.7)
El foco del teclado debe estar visualmente indicado.
/* Nunca quites indicadores de foco sin reemplazo */
*:focus {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
/* Estilo de foco personalizado para elementos interactivos */
button:focus-visible,
a:focus-visible {
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.4);
}
Bloques de Omisión (2.4.1)
Proporciona una forma de saltar contenido repetitivo.
<!-- Enlace de omisión para usuarios de teclado -->
<a href="#main-content" class="skip-link">Saltar al contenido principal</a>
<nav><!-- navegación --></nav>
<main id="main-content">
<!-- contenido principal -->
</main>
Comprensible
Texto Legible (3.1.5)
El contenido debería ser legible a nivel de educación secundaria inferior.
<!-- Bien: lenguaje claro y simple -->
<p>Ingresa tu correo para restablecer tu contraseña.</p>
<!-- Mal: jerga y complejidad -->
<p>Introduzca su dirección de correo electrónico registrada para iniciar el protocolo de recuperación de credenciales.</p>
Prevención de Errores (3.3.4)
Prevenir errores en envíos legales/financieros/de modificación de datos.
<!-- Bien: confirmación para acciones destructivas -->
<form onsubmit="return confirm('Eliminar esta cuenta permanentemente?')">
<button type="submit">Eliminar Cuenta</button>
</form>
<!-- Bien: revisar antes del envío final -->
<form>
<fieldset>
<legend>Revisa tu pedido</legend>
<!-- resumen del pedido -->
</fieldset>
<button type="submit">Confirmar Pago</button>
</form>
Confiable
HTML y ARIA Válidos (4.1.1, 4.1.2)
Usa marcado válido y roles ARIA apropiados.
<!-- Bien: elemento semántico nativo -->
<nav aria-label="Navegación principal">
<ul>
<li><a href="/" aria-current="page">Inicio</a></li>
<li><a href="/about">Acerca de</a></li>
</ul>
</nav>
<!-- Bien: ARIA para componentes personalizados -->
<div role="tablist">
<button role="tab" aria-selected="true" id="tab-1">Pestaña 1</button>
<button role="tab" aria-selected="false" id="tab-2">Pestaña 2</button>
</div>
<div role="tabpanel" aria-labelledby="tab-1">Contenido del panel</div>
Pruebas de Accesibilidad
| Herramienta | Propósito |
|---|---|
| axe DevTools | Extensión de navegador para verificaciones automatizadas |
| Lighthouse | Auditoría de accesibilidad integrada en Chrome |
| NVDA / JAWS | Pruebas con lector de pantalla (Windows) |
| VoiceOver | Pruebas con lector de pantalla (macOS) |
| Tecla Tab | Prueba manual de navegación por teclado |
| WAVE | Herramienta de evaluación de accesibilidad de WebAIM |
Errores Comunes
- Usar
outline: nonesin reemplazo — los usuarios de teclado pierden su lugar - Depender únicamente del color para errores — agrega íconos y texto
- Faltar etiquetas en formularios — cada entrada necesita una etiqueta asociada
- Reproducción automática de medios sin controles — respeta
prefers-reduced-motion - Scroll infinito sin mecanismo de omisión — proporciona paginación o búsqueda
FAQ
Qué nivel de WCAG debería apuntar? Nivel AA es el estándar para la mayoría de requisitos legales. Nivel AAA es aspiracional para contenido crítico.
Necesito probar con lectores de pantalla reales? Las herramientas automatizadas detectan ~30% de los problemas. Las pruebas manuales con lectores de pantalla y navegación por teclado encuentran el resto.
Cómo manejo contenido en vivo (SPA, AJAX)? Usa regiones ARIA live para anunciar actualizaciones, gestiona el foco en cambios de ruta y asegura que los modales atrapen el foco.
¿Cómo empiezo con esto en un proyecto existente?
Empieza con una parte pequeña y aislada de tu codebase. Aplica los conceptos de esta guía a un módulo o servicio. Mide el impacto, luego expande a otras áreas.
¿Qué herramientas necesito?
Las herramientas mencionadas throughout esta guía se listan en cada sección. La mayoría son open-source y ampliamente adoptadas. Consulta los recursos relacionados para instrucciones de setup.
¿Cómo mido el éxito después de implementar esto?
Define métricas claras antes de empezar: benchmarks de rendimiento, tasas de error o indicadores de mantenibilidad. Compara antes y después. Itera basándote en datos, no en suposiciones.
Temas Avanzados
Escenario: Auditoria WCAG 2.2 para E-commerce
Sistema: Tienda online, 50 paginas, 200 componentes
Objetivo: WCAG 2.2 nivel AA completo
Auditoria automatizada (axe-core):
npm install --save-dev @axe-core/playwright
test("homepage cumple WCAG", async ({ page }) => {
await page.goto("https://shop.example.com");
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"])
.analyze();
expect(results.violations).toEqual([]);
});
// Resultados por categoria:
// | Regla | Violaciones | Severidad |
// |-------|-------------|-----------|
// | color-contrast | 12 | Critical |
// | aria-label | 5 | Serious |
// | heading-order | 3 | Moderate |
// | image-alt | 8 | Critical |
// | tab-order | 2 | Serious |
// | focus-visible | 4 | Serious |
Auditoria manual (checklist):
| Categoria | Item | Estado |
|-----------|------|--------|
| Perceptible | Texto alternativo en imagenes | Pendiente |
| Perceptible | Contraste de color > 4.5:1 | Pendiente |
| Perceptible | Texto redimensionable hasta 200% | Pendiente |
| Operable | Navegacion por teclado completa | Pendiente |
| Operable | Focus visible en todos los elementos | Pendiente |
| Operable | Sin time-outs sin opcion de extender | Pendiente |
| Operable | Skip to main content link | Pendiente |
| Comprensible | Labels en todos los formularios | Pendiente |
| Comprensible | Identificacion de errores en formularios | Pendiente |
| Comprensible | Lenguaje de pagina declarado | Pendiente |
| Robusto | ARIA roles correctos | Pendiente |
| Robusto | Compatible con lectores de pantalla | Pendiente |
Herramientas:
| Herramienta | Tipo | Uso |
|-------------|------|-----|
| axe-core | Automatico | CI/CD + E2E tests |
| Lighthouse | Automatico | Auditoria rapida |
| NVDA | Manual | Lector de pantalla Windows |
| VoiceOver | Manual | Lector de pantalla macOS |
| WAVE | Automatico | Extension navegador |
| keyboard-nav | Manual | Navegacion solo teclado |
Lecciones:
- Automatiza lo que puedas, pero la auditoria manual es obligatoria
- axe-core en CI/CD previene regresiones
- Probar con lectores de pantalla reales es indispensable
- El contraste de color es la violacion mas comun
- WCAG 2.2 AA es el estandar legal en muchos paises
Como hago un componente modal accesible?
Usa role="dialog" y aria-modal="true". Gestiona el focus: muevelo al primer elemento interactivo al abrir, atrapalo dentro del modal, y restauralo al elemento que abrio el modal al cerrar. Cierra con Escape. Usa inert en el resto de la pagina. Ejemplo: <div role="dialog" aria-modal="true" aria-labelledby="title">. Probar con teclado y lector de pantalla.
Recursos Relacionados
Progressive Web Apps (PWA) — Referencia Detallada
Referencia Detallada para construir Progressive Web Apps: service workers, soporte offline, Web App Manifest, notificaciones push e instalabilidad.
GuideWeb Components — Custom Elements, Shadow DOM y Templates
Guía práctica de Web Components: crear elementos personalizables reutilizables, encapsular estilos con Shadow DOM y componer con templates HTML.
PatternPatrón Circuit Breaker
Previene fallos en cascada deteniendo solicitudes a servicios que están fallando. Un patrón arquitectural para sistemas distribuidos resilientes.