Accesibilidad en la Interfaz Generativa: cómo hacer interfaces de IA inclusivas
Guía práctica para hacer interfaces generativas accesibles para todos los usuarios, incluyendo lectores de pantalla y navegación por teclado.
Por qué la accesibilidad es más difícil en la Interfaz Generativa
Tu equipo de accesibilidad acaba de firmar la revisión de cada pantalla del producto. Tres semanas después, la IA genera un layout que ningún diseñador dibujó — y en ese layout la jerarquía de encabezados se rompe para los lectores de pantalla, el diálogo generado crea una trampa de foco, y el gráfico usa el color como única señal. Nada de esto quedó atrapado en la revisión porque nada de esto existía en ese momento.
Esta es la nueva superficie de accesibilidad, y el manual antiguo no la cubre.
En una interfaz de usuario tradicional, un ingeniero puede auditar cada pantalla y verificar que cumple los requisitos WCAG 2.2. El número de pantallas es finito. El equipo de accesibilidad (a11y) sabe exactamente qué tiene que probar.
La Interfaz Generativa rompe este modelo. El conjunto de interfaces posibles no es enumerable — la IA puede componer componentes de maneras que ningún humano diseñó explícitamente. Una pantalla que pasa la revisión de accesibilidad hoy podría combinarse mañana con un componente recién añadido y producir un layout inaccesible.
La solución es trasladar los requisitos de accesibilidad al nivel del componente. Si cada componente de tu biblioteca es individualmente accesible, cualquier composición de ellos también lo será — siempre que la composición en sí esté estructurada correctamente. Esta salvedad tiene mucho peso; volvemos a ella en la sección «Problemas combinatorios de accesibilidad», porque ahí es donde viven la mayoría de los bugs reales de a11y en sistemas generativos.
En la práctica, este es un modelo más limpio que auditar manualmente cada pantalla. Además, no es negociable: la IA no añadirá atributos ARIA ni gestionará el foco por ti. La biblioteca de componentes es tu único punto de control.
La línea base a nivel de componente
Cada componente en tu registro de herramientas de Interfaz Generativa debe cumplir estos requisitos de forma independiente:
HTML semántico primero. Usa <button> para botones, <nav> para la navegación, <table> para datos tabulares. No uses <div onClick={...}> cuando existe un elemento semántico adecuado.
// Incorrecto: un div disfrazado de botón
<div className="button" onClick={handleClick}>Submit</div>
// Correcto: elemento botón real
<button type="button" onClick={handleClick}>Submit</button>
Todas las imágenes tienen texto alternativo. Para imágenes decorativas: alt="". Para imágenes informativas, escribe una descripción.
El color no es la única señal. Un gráfico que muestra valores positivos en verde y negativos en rojo necesita otro indicador para los usuarios que no pueden distinguir ambos colores — un signo + / -, un icono o una etiqueta de texto.
function TrendIndicator({ value }: { value: number }) {
const isPositive = value >= 0;
return (
<span
className={isPositive ? 'text-green-600' : 'text-red-600'}
aria-label={isPositive ? `Up ${Math.abs(value)}%` : `Down ${Math.abs(value)}%`}
>
{/* El icono proporciona una señal visual más allá del color */}
{isPositive ? '↑' : '↓'} {Math.abs(value)}%
</span>
);
}
Los elementos interactivos son accesibles por teclado. Cada botón, enlace y control de formulario en tus componentes debe ser enfocable y completamente operable con el teclado.
Los objetivos táctiles son suficientemente grandes. WCAG 2.2, criterio 2.5.8 (Target Size, Minimum, nivel AA) exige un mínimo de 24×24 píxeles CSS; el anterior WCAG 2.1, criterio 2.5.5 (AAA), recomienda 44×44. Para acciones principales en móvil, apunta al nivel AAA — los botones pequeños siguen siendo una de las principales causas de fallos de accesibilidad.
Regiones ARIA live para contenido en streaming
El streaming es la característica definitoria de la Interfaz Generativa — los componentes aparecen de forma progresiva a medida que la IA los genera. Los lectores de pantalla no anuncian automáticamente el contenido que aparece de forma dinámica. Tienes que indicárselo.
Usa aria-live para anunciar cuándo llega nuevo contenido generado:
// components/genui-output-region.tsx
export function GenUIOutputRegion({ children, isLoading }: {
children: React.ReactNode;
isLoading: boolean;
}) {
return (
<div
aria-live="polite"
aria-busy={isLoading}
aria-label="Contenido generado por IA"
aria-atomic="false"
>
{children}
</div>
);
}
Decisiones clave:
aria-live="polite"anuncia el nuevo contenido en el próximo momento de inactividad — sin interrumpir al usuario a mitad de una frase como haríaassertive.aria-busy={isLoading}le indica a la tecnología de asistencia que la región se está actualizando. Los lectores de pantalla retienen los anuncios hasta quearia-busypasa a serfalse.aria-atomic="false"anuncia cada adición individualmente a medida que llega, en lugar de releer toda la región cada vez.
Para el estado de carga con skeleton:
function LoadingSkeleton({ label }: { label: string }) {
return (
<div
role="status"
aria-label={`Cargando ${label}`}
className="animate-pulse rounded-lg bg-muted h-32"
/>
);
}
role="status" es una región implícita aria-live="polite" para mensajes de estado breves. Se anuncia cuando aparece sin interrumpir el discurso en curso.
Gestión del foco
Cuando aparece contenido generado, el foco del teclado permanece donde estaba. Habitualmente esto es correcto — no quieres que el foco salte mientras la IA transmite componentes. Sin embargo, en algunas interacciones necesitas mover el foco de forma explícita.
Después de un envío de formulario que reemplaza el contenido de la página:
const outputRef = useRef<HTMLDivElement>(null);
const [generatedUI, setGeneratedUI] = useState<React.ReactNode>(null);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const ui = await generateUI(prompt);
setGeneratedUI(ui);
}
// Mueve el foco DESPUÉS de que React haya confirmado el nuevo DOM — nunca con setTimeout.
useEffect(() => {
if (generatedUI) {
outputRef.current?.focus();
}
}, [generatedUI]);
// tabIndex={-1} hace el div enfocable mediante código
<div ref={outputRef} tabIndex={-1} aria-label="Resultados generados">
{generatedUI}
</div>
tabIndex={-1} hace que el elemento sea enfocable mediante código sin añadirlo al orden de tabulación. El usuario puede pasar de largo tabulando normalmente, pero puedes enfocarlo con .focus().
Evita el antipatrón habitual de setTimeout(() => ref.current?.focus(), 50). 50 ms es solo una suposición; si el render tarda más en un dispositivo lento, la llamada a .focus() irá a un elemento obsoleto o inexistente. useEffect se ejecuta después de que React haya confirmado el nuevo DOM — exactamente la garantía que necesitas. Si realmente hace falta diferir un tíc más (por ejemplo, esperando un portal hijo), usa queueMicrotask, pero nunca un timeout con un número mágico.
Después de que un diálogo o panel se abre con contenido generado:
Mueve el foco al primer elemento enfocable dentro del panel, o al encabezado del panel. Devuelve el foco al elemento que lo activó cuando el panel se cierra.
Navegación por teclado en componentes generados
Los componentes que aparecen en los layouts generados deben ser completamente navegables por teclado. Audita cada componente:
Tablas: Los usuarios de lectores de pantalla esperan navegación con las teclas de flecha dentro de las celdas. Si tu componente DataTable no implementa esto, supone una barrera de teclado para tablas complejas.
Gráficos: Proporciona una alternativa tabular. Los gráficos SVG son visualmente ricos pero casi ininterpretables para los lectores de pantalla. Añade un elemento <details> o una tabla visualmente oculta con los datos del gráfico.
function BarChart({ title, data }: BarChartProps) {
return (
<div>
<h3>{title}</h3>
{/* Gráfico visual */}
<svg aria-hidden="true">
{/* ... renderizado del gráfico ... */}
</svg>
{/* Tabla de datos accesible, visualmente oculta */}
<details className="sr-only">
<summary>Ver datos como tabla</summary>
<table>
<caption>{title}</caption>
<thead>
<tr><th>Categoría</th><th>Valor</th></tr>
</thead>
<tbody>
{data.map(({ label, value }) => (
<tr key={label}>
<td>{label}</td>
<td>{value}</td>
</tr>
))}
</tbody>
</table>
</details>
</div>
);
}
La clase sr-only oculta la tabla visualmente pero la mantiene en el árbol de accesibilidad. aria-hidden="true" en el SVG evita que los lectores de pantalla intenten interpretar el marcado SVG en bruto.
Movimiento reducido
Algunos usuarios configuran su sistema operativo para preferir movimiento reducido — porque las animaciones causan molestias físicas a personas con trastornos vestibulares. Los skeletons de carga y las animaciones de transición deben respetar esta preferencia.
/* En tu CSS global o configuración de Tailwind */
@media (prefers-reduced-motion: reduce) {
.animate-pulse {
animation: none;
}
.transition-all {
transition: none;
}
}
En Tailwind, puedes usar las variantes motion-safe: y motion-reduce::
<div className="motion-safe:animate-pulse motion-reduce:opacity-50 bg-muted rounded-lg h-32" />
motion-safe: se aplica solo cuando el usuario no ha solicitado movimiento reducido. motion-reduce: se aplica cuando sí lo ha solicitado. Para los estados de carga, un marcador de posición estático ligeramente atenuado es una buena alternativa a la animación pulsante cuando el movimiento reducido está activado.
Jerarquía de encabezados en layouts compuestos
La IA compone componentes formando layouts. Cada componente puede tener su propio encabezado. Cuando varios componentes aparecen juntos, sus encabezados deben formar una jerarquía coherente — no una mezcla de H2 inconexos.
Este es un problema de composición que no puede resolverse a nivel de componente individual. Cada componente necesita aceptar una prop con el nivel del encabezado:
interface MetricCardProps {
label: string;
value: string;
change: number;
headingLevel?: 'h2' | 'h3' | 'h4'; // por defecto h3
}
function MetricCard({ label, value, change, headingLevel: Heading = 'h3' }: MetricCardProps) {
return (
<div className="rounded-lg border p-6">
<Heading className="text-sm font-medium text-muted-foreground">{label}</Heading>
{/* ... */}
</div>
);
}
En la definición de tu herramienta, incluye el nivel del encabezado como un parámetro que la IA puede establecer:
metricCard: {
description: 'Muestra una métrica KPI. Usa headingLevel h2 para la primera métrica de una sección, h3 para las siguientes.',
parameters: z.object({
label: z.string(),
value: z.string(),
change: z.number(),
headingLevel: z.enum(['h2', 'h3', 'h4']).default('h3'),
}),
}
Problemas combinatorios de accesibilidad
El modelo «componente accesible → composición accesible» tiene un límite claro: dos componentes que pasan axe por separado pueden violar WCAG juntos al renderizarse en paralelo. Estos son bugs que solo existen en sistemas generativos y no aparecerán en ningún test por componente individual.
Ruptura de jerarquía de encabezados. El componente A renderiza un H2. El componente B también renderiza un H2. La IA los coloca uno al lado del otro en una cuadrícula de tarjetas. El resultado: el lector de pantalla reporta dos secciones del mismo nivel que deberían haber sido H3 hijos de un H2 padre. Mitigación: parametrizar los niveles de encabezado (sección anterior) y añadir un test de integración que recorra el árbol y verifique la monotonía de los niveles.
Conflictos en la jerarquía ARIA. El componente Dialog establece aria-modal="true". La IA anida otro Dialog dentro (al modelo se le encargó renderizar una confirmación dentro del panel). La pila tiene dos modales — el comportamiento de las tecnologías de asistencia queda indefinido. Mitigación: detectar aria-modal anidados en el momento del render, rechazar el render del diálogo interior y registrar una advertencia en desarrollo.
Duplicación de etiquetas. Dos componentes SearchInput en la misma página generada producen <label>Search</label>. Ambos inputs tienen el mismo nombre accesible — el usuario de lector de pantalla no puede distinguirlos. Mitigación: hacer la prop label obligatoria (sin valores por defecto) y exigir explícitamente en el prompt de la IA que nombre cada instancia.
Acumulación de regiones live. Tres subcomponentes en streaming se envuelven cada uno en aria-live="polite". El lector de pantalla encola tres anuncios superpuestos. Mitigación: solo la región de salida generativa más externa declara aria-live; los componentes hijos transmiten como DOM normal dentro de ella.
Estos bugs no son teóricos — son el modo de fallo habitual de los sistemas de «compón lo que quieras». Se corrigen a nivel de integración: tomar instantáneas de una muestra representativa de layouts generados, ejecutar axe sobre los árboles combinados y añadir verificaciones personalizadas para los cuatro patrones anteriores.
Pruebas con usuarios reales
Las herramientas automatizadas — axe-core, jest-axe, el addon de Storybook a11y, Lighthouse — detectan aproximadamente el 30 % de los problemas de accesibilidad. (Esta es la propia estimación de Deque Systems para axe-core, y coincide con lo que dirá cualquier consultora de accesibilidad.) El 70 % restante son cuestiones de juicio: ¿es comprensible el texto que se anuncia? ¿Coincide el orden del foco con el orden visual que espera un usuario con visión? ¿Puede un usuario de lector de pantalla completar realmente la tarea?
A esas preguntas no responde ninguna tarea de CI. Se necesitan personas reales.
Lista de verificación operativa para pruebas con usuarios reales en un lanzamiento de Interfaz Generativa:
- Recorrido con lector de pantalla — NVDA en Windows + Firefox. La combinación más utilizada entre usuarios de lectores de pantalla en el mundo (encuesta WebAIM). Ejecuta los 5 principales escenarios generativos.
- Recorrido con lector de pantalla — VoiceOver en macOS + Safari y VoiceOver en iOS + Safari. Apple domina entre los lectores de pantalla móviles.
- Recorrido solo con teclado. Desconecta el ratón. Completa cada tarea principal con Tab, Shift+Tab, Enter, Espacio, Escape y las teclas de flecha. Anota cada indicador de foco que desaparezca y cada trampa de teclado.
- Recorrido con control por voz. Voice Control en macOS o Dragon. La Interfaz Generativa tiene fama de ser difícil para el control por voz — las etiquetas las genera la IA, lo que saca a la luz defectos de nomenclatura que de otro modo no se detectarían.
- Participantes reales. Incorpora de 2 a 4 usuarios de lectores de pantalla por trimestre — a través de Fable, AccessWorks o una comunidad local de a11y. Una sola sesión vale más que cien ejecuciones automatizadas.
- Alto contraste y zoom. Windows High Contrast + zoom del navegador al 200 % + zoom al 400 % con reflow. Los layouts generativos suelen romperse con zoom alto porque la IA emite anchos fijos.
- Movimiento reducido. Activa la preferencia del sistema y vuelve a ejecutar los escenarios de streaming.
Asigna presupuesto a esto. Una cadencia razonable para un equipo pequeño: verificaciones automatizadas en cada PR, un recorrido manual de cuatro horas antes de cada lanzamiento, y una sesión externa pagada con participantes con discapacidad una vez al trimestre.
ROI: cómo justificarlo ante la dirección de ingeniería
El trabajo de accesibilidad compite por el tiempo de ingeniería con nuevas funcionalidades. Si eres responsable de ingeniería, necesitas cifras — y hay que presentarlas en el idioma que entiende el director financiero.
Coste. Incorporar la accesibilidad a la biblioteca de componentes en la fase de diseño supone aproximadamente un 5–10 % del coste de desarrollo del componente (estimaciones de Forrester, equipos de a11y en Microsoft). Retrofitar una biblioteca inaccesible después del lanzamiento cuesta un 30–100 %: reconstruyes componentes mientras saldas la deuda con todos los consumidores afectados. El componente accesible más barato es el que escribiste accesible desde el principio.
Riesgo. En el marco de la European Accessibility Act (EAA), la aplicación comenzó el 28 de junio de 2025: los servicios digitales B2C que se venden en la UE deben cumplir EN 301 549 (alineado con WCAG 2.1 AA). Las sanciones se determinan a nivel de cada Estado miembro, pero en algunas jurisdicciones alcanzan seis cifras en euros por infracción. La ADA en EE. UU. genera alrededor de 4.000+ demandas anuales por accesibilidad web (informe anual de UsableNet); el acuerdo medio ronda los 15.000–50.000 dólares más las correcciones obligatorias. La UK Equality Act, la ACA canadiense y la DDA australiana añaden una exposición comparable. Una Interfaz Generativa que emite masivamente layouts no conformes es, en esencia, un generador probabilístico de demandas.
Ingresos. Aproximadamente el 16 % de la población mundial vive con alguna discapacidad significativa (OMS, 2023). El estudio «Click-Away Pound» del Reino Unido estimó pérdidas de 17.100 millones de libras al año — dinero que los compradores no dejan en tiendas inaccesibles. Los contratos públicos en la UE, EE. UU. y Canadá exigen conformidad con la Sección 508 / EN 301 549; un producto inaccesible no puede participar en licitaciones.
Plazos de implementación, por orden de prioridad. Plan de 90 días para una Interfaz Generativa ya existente:
| Semana | Trabajo | Días-persona |
|---|---|---|
| 1–2 | Auditoría del registro de componentes con axe + recorrido manual con lector de pantalla; lista de defectos por componente | 5–8 |
| 3–4 | Corregir los 10 componentes más críticos (semántica HTML, foco, etiquetas) | 8–12 |
| 5–6 | Añadir salida aria-live global, gestión del foco y soporte de movimiento reducido a nivel de layout | 4–6 |
| 7–8 | Parametrizar niveles de encabezado; añadir tests de integración combinatorios | 4–6 |
| 9–10 | Integrar jest-axe + addon de Storybook a11y en CI; bloquear merges ante regresiones | 2–3 |
| 11–12 | Primera sesión externa con usuarios de lectores de pantalla; corregir lo que encuentren | 3–5 |
| A partir de entonces | Pruebas de usuario trimestrales, verificaciones automáticas de deriva semanales | 1 día / semana |
En total: aproximadamente 30–45 días-persona para obtener una línea base significativa en una biblioteca de componentes de tamaño medio, más mantenimiento continuo. Plantéalo como una inversión de un trimestre que elimina toda una clase recurrente de riesgos legales, de ingresos y reputacionales.
Matriz de prioridades para el triaje.
| Alto impacto en el usuario | Bajo impacto en el usuario | |
|---|---|---|
| Alto riesgo legal | Corregir este trimestre | Corregir este semestre |
| Bajo riesgo legal | Corregir este semestre | Al backlog con fecha |
El riesgo legal es alto cuando la infracción afecta a un flujo transaccional (pago, registro, gestión de cuenta) o a cualquier superficie de uso público. El impacto en el usuario es alto cuando el bug bloquea completar la tarea para usuarios de tecnologías de asistencia, no solo empeora la comodidad.
Herramientas de prueba
Usa estas herramientas para auditar tu biblioteca de componentes y las salidas generadas. Las versiones indicadas son las vigentes a mediados de 2025 — consulta las actuales antes de implementar.
axe-core (axe-core@4.x, jest-axe@9.x): Pruebas de accesibilidad automatizadas que detectan aproximadamente el 30 % de los problemas. Intégralo con jest-axe para cobertura en pruebas unitarias.
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('MetricCard no tiene violaciones de accesibilidad', async () => {
const { container } = render(
<MetricCard label="Revenue" value="$84,200" change={12.4} />
);
expect(await axe(container)).toHaveNoViolations();
});
Addon de accesibilidad de Storybook (@storybook/addon-a11y@8.x): Ejecuta comprobaciones de axe directamente en Storybook durante el desarrollo. Detecta problemas antes de que lleguen a los tests.
Pruebas con lector de pantalla: NVDA (Windows, gratuito) y VoiceOver (macOS, integrado) son indispensables para probar la experiencia que las herramientas automatizadas no pueden medir — ¿resulta comprensible el contenido generado cuando se lee en voz alta? La lista de verificación ampliada está en la sección «Pruebas con usuarios reales» más arriba.
Navegación solo con teclado: Desconecta el ratón y navega por tu aplicación usando únicamente Tab, Shift+Tab, Enter, Espacio y las teclas de flecha. Es la forma más rápida de encontrar trampas de teclado.
Resumen de los requisitos innegociables
Antes de publicar una funcionalidad de Interfaz Generativa:
- Cada componente del registro de herramientas pasa axe sin violaciones
- Todos los elementos interactivos son accesibles y completamente operables por teclado
- El color nunca es la única señal de significado
- La salida en streaming está envuelta en una región
aria-live(y solo la región más externa la declara) - Los skeletons tienen
role="status"y unaria-labeldescriptivo - Los gráficos SVG tienen una alternativa de datos tabulares
- Todas las animaciones respetan
prefers-reduced-motion - Los niveles de encabezado están parametrizados en los componentes, no codificados fijamente
- Los tests de integración combinatorios cubren al menos los cuatro patrones anteriores
- Al menos una sesión externa de pruebas de usuario con usuarios de lectores de pantalla por trimestre
La accesibilidad integrada en la biblioteca de componentes no es una carga — es lo que hace que la promesa de «la IA puede componer cualquier cosa» sea verdadera para todos los usuarios. Y es lo que te mantiene fuera de los tribunales.
Materiales relacionados: guía práctica (Interfaz Generativa con React — guía práctica) y guía de rendimiento (Optimización del rendimiento en la Interfaz Generativa).
¿Construyendo Interfaz Generativa accesible para una aplicación compleja? Trabajemos juntos en los detalles específicos.
Alex
Ingeniero y Consultor de Generative UI
Ingeniero senior especializado en interfaces con AI y sistemas Generative UI. Ayudando a equipos de producto a lanzar más rápido con el stack GenUI adecuado.
Artículos relacionados
Κατασκευάζοντας το Πρώτο σας Generative UI με το Vercel AI SDK
Βήμα-βήμα οδηγός για τη δημιουργία της πρώτης σας AI-powered διεπαφής με streaming συστατικά.
Προσβασιμότητα σε Generative UI: Δημιουργία Συμπεριληπτικών AI Διεπαφών
Πρακτικός οδηγός για προσβάσιμα γεννητικά interfaces — screen readers, πλοήγηση με πληκτρολόγιο και συνδυαστικά προβλήματα προσβασιμότητας.
CopilotKit vs Vercel AI SDK vs Thesys: Σύγκριση Frameworks
Μια ειλικρινής σύγκριση των τριών κύριων frameworks Generative UI, με πλεονεκτήματα, μειονεκτήματα και πότε να χρησιμοποιείτε το καθένα.
Adelántate en Generative UI
Artículos semanales, actualizaciones de frameworks y guías de implementación prácticas — directamente a tu bandeja de entrada.
¿Necesitas ayuda para implementar lo que acabas de leer?
Reserva una consulta gratuita