Por qué la Accesibilidad no es sobre "marcar casillas", sino sobre personas
\nSeamos honestos: cuando escuchamos "accesibilidad" (o a11y), muchos de nosotros imaginamos listas interminables de requisitos, revisiones aburridas y la frase "bueno, esto es para personas con discapacidades". Pero la verdad es que la accesibilidad es para todos. ¿Alguna vez has intentado pedir una pizza mientras caminas, sosteniendo el teléfono con una sola mano? ¿O has estado en el metro con mala conexión a internet, donde el sitio tarda una eternidad en cargar? Eso son problemas de accesibilidad.
\nEn este artículo hablaremos de cómo TypeScript puede convertirse en tu mejor amigo para crear aplicaciones web accesibles. No solo "agregar etiquetas", sino construir una arquitectura donde la inclusividad no sea una característica, sino la base.
\n\nTypeScript como escudo contra errores de a11y
\nMuchos piensan que TypeScript es solo sobre tipos de datos y protección contra errores. Pero en realidad, la tipificación estricta también puede ayudar en cuestiones de accesibilidad. Analicemos con ejemplos.
\n\nTipificación de atributos ARIA
\nUno de los problemas más comunes son los valores incorrectos de los atributos ARIA. ¿Alguna vez has escrito role="button" en un div? ¿Y luego olvidaste agregar tabindex? TypeScript puede detectar esto en tiempo de compilación.
// Mal: sin protección alguna\nfunction createButton(label: string) {\n return `<div role="button">${label}</div>`;\n}\n\n// Bien: tipificamos ARIA\ntype AriaRole = 'button' | 'navigation' | 'alert' | 'dialog';\n\ninterface AriaAttributes {\n role: AriaRole;\n 'aria-label'?: string;\n 'aria-expanded'?: boolean;\n tabIndex?: number;\n}\n\nfunction createAccessibleButton(label: string, aria: AriaAttributes) {\n // Ahora TypeScript verificará que el rol sea válido\n return {\n tag: 'div',\n attrs: aria,\n content: label\n };\n}\n\n// ¡Error! 'btn' no está incluido en el tipo AriaRole\n// createAccessibleButton('Haz clic en mí', { role: 'btn' });\n¿Notas la diferencia? TypeScript no te permitirá escribir accidentalmente role="btutton" u olvidar un atributo obligatorio. Es como un asistente personal que susurra: "Oye, ¿estás seguro de que esto es accesible?"
Verificación estricta de estados de enfoque
\nOtro dolor de cabeza es la gestión del foco. Especialmente en SPA, donde la página no se recarga. TypeScript ayuda a crear envoltorios seguros.
\n// Definimos qué elementos pueden recibir el foco\ntype FocusableElement = HTMLInputElement | HTMLButtonElement | HTMLAnchorElement | HTMLSelectElement | HTMLTextAreaElement;\n\nfunction safelySetFocus(element: FocusableElement | null): void {\n if (element && document.contains(element)) {\n element.focus();\n } else {\n console.warn('El elemento no se encuentra en el DOM o no puede recibir el foco');\n }\n}\n\n// Uso\nconst modal = document.querySelector('.modal__close-btn');\nsafelySetFocus(modal as FocusableElement | null);\nObserva: no solo llamamos a .focus(), sino que verificamos si el elemento existe y si puede ser enfocado. Esto previene errores que podrían dejar al usuario de un lector de pantalla en el vacío.
Construyendo una arquitectura con a11y como base
\nAhora pasemos a cosas más avanzadas. ¿Cómo lograr que la accesibilidad esté integrada en la arquitectura de tu proyecto, y no añadida "a última hora" antes del lanzamiento?
\n\nProps tipificados para componentes
\nCrea una interfaz base para todos tus componentes. Es como una constitución para la accesibilidad.
\n// Interfaz base para todos los componentes UI\ninterface AccessibleComponentProps {\n id?: string;\n className?: string;\n ariaLabel?: string;\n ariaDescribedby?: string;\n role?: string;\n tabIndex?: number;\n isDisabled?: boolean;\n onClick?: () => void;\n onKeyDown?: (e: React.KeyboardEvent) => void;\n}\n\n// Componente de botón específico\ninterface ButtonProps extends AccessibleComponentProps {\n variant