Componentes UI accesibles

Ilustración de componentes UI accesibles: botón con texto claro, menú desplegable con alto contraste, opciones de radio con etiquetas visibles y campo de texto con borde definido, representando buenas prácticas de accesibilidad web.

Diseñar y desarrollar componentes UI accesibles no va de “cumplir WCAG para pasar una auditoría”. Va de algo mucho más práctico: hacer que tu interfaz sea operable, predecible y entendible para más gente, en más contextos (teclado, lector de pantalla, zoom, baja visión, movilidad reducida, fatiga, distracciones, etc.). Eso es a11y aplicada al desarrollo web.

Y aquí viene la parte que suele doler: un componente “bonito” puede ser una máquina de carga cognitiva. Y uno “funcional” puede disparar el tiempo de decisión. En accesibilidad web (y, en particular, en accesibilidad visual), tu trabajo consiste en equilibrar ambas:

  • Tiempo de decisión: ¿cuánto tarda una persona en entender qué opciones tiene y cuál elegir?
  • Carga cognitiva: ¿cuánta energía mental cuesta orientarse, recordar estados, y mantener el contexto mientras interactúa?

Un modal mal hecho rompe el contexto (sube carga cognitiva). Un carrusel que se mueve solo pelea por la atención (sube carga cognitiva). Un dropdown custom para elegir país con 200 opciones puede reducir tiempo de decisión (si permite búsqueda) o multiplicarlo (si es un infierno de teclado). Lo accesible no es “más ARIA”; es mejores decisiones de interacción + semántica sólida.

Antes de tocar ARIA: cuatro reglas que te ahorran bugs (y tickets)

1) Semántica primero, ARIA después

Si puedes resolverlo con HTML nativo, hazlo. La semántica correcta hace que la estructura y relaciones sean “detectables” por tecnologías de asistencia, en línea con el espíritu de WCAG sobre información y relaciones.

2) No crees trampas de teclado

Si alguien puede “entrar” a un componente con Tab, debe poder salir sin magia ni ratón. Esto es especialmente crítico en modales, menús y widgets compuestos.

3) El foco debe contar una historia lógica

El orden de foco tiene que preservar significado y operabilidad: primero lo importante, después lo secundario, y siempre coherente con el flujo real de la tarea.

4) Los cambios de estado deben anunciarse sin interrumpir

Las notificaciones y mensajes de estado existen para informar sin robar foco. WCAG trata esto explícitamente en “status messages”.

Traducción práctica: si “arreglas” la accesibilidad metiendo role y aria-* a lo loco, pero rompes foco, teclado y estados, has empeorado la UX y la a11y a la vez.

Modales accesibles: checklist completo (focus trap, aria, escape, scroll)

Los modales son el ejemplo perfecto del choque tiempo de decisión vs carga cognitiva: te obligan a decidir ahora, pero a cambio te sacan del flujo. Por eso, el primer criterio de accesibilidad de un modal… es no usarlo si no hace falta.

Cuándo sí (y cuándo no)

Sí:

  • Confirmaciones destructivas (“Eliminar proyecto”).
  • Flujos breves con dependencia (“Aceptar cookies con opciones avanzadas”).
  • Formularios cortos que no ameritan navegación a otra página.

No (mejor alternativa):

  • Información extensa → mejor página, panel lateral o sección expandible.
  • Errores o avisos → mejor toast/status + foco en el campo problemático.
  • Menús de navegación → mejor navegación real, no modal.

Estructura y ARIA mínima (la que de verdad importa)

Patrón recomendado: modal dialog (no “div flotante”). El patrón del APG define que el diálogo modal debe contener su propia secuencia de tabulación (Tab/Shift+Tab no deberían salir del modal mientras esté abierto).

Requisitos:

  • Contenedor con role="dialog" (o role="alertdialog" si es una decisión crítica inmediata).
  • aria-modal="true" cuando es modal.
  • Etiqueta accesible: aria-labelledby apuntando al título.
  • Descripción opcional: aria-describedby apuntando a un texto breve (no a un párrafo eterno).
<button id="open">Abrir</button>

<div
  role="dialog"
  aria-modal="true"
  aria-labelledby="dialog-title"
  aria-describedby="dialog-desc"
  hidden
>
  <h2 id="dialog-title">Eliminar proyecto</h2>
  <p id="dialog-desc">Esta acción no se puede deshacer.</p>

  <button>Cancelar</button>
  <button>Eliminar</button>
</div>

Nota: el APG explica que aria-modal puede reemplazar la técnica de “ocultar” el fondo con aria-hidden para indicar que el contenido exterior queda inerte.

Focus trap y restauración de foco (la parte que casi siempre se rompe)

Un modal accesible hace cuatro cosas, siempre:

  1. Guarda el elemento que tenía foco antes de abrir.
  2. Mueve foco dentro del modal (ideal: título o primer control significativo).
  3. Atrapa el foco dentro (Tab y Shift+Tab ciclan).
  4. Restauras el foco al cerrarlo (vuelves al botón “Abrir”, por ejemplo).

Esto además evita caer en “trampa de teclado” en el sentido de WCAG: si el modal no ofrece una forma clara de salir (p. ej. Escape y botón cerrar), estás creando un problema serio.

Interacciones de teclado esperadas

  • Escape cierra (salvo casos muy justificados).
  • Tab/Shift+Tab no sale del modal (cicla dentro).

Scroll: el enemigo silencioso

Los modales suelen fallar en tres escenarios:

  • Contenido largo + viewport pequeño (móvil, zoom 200%).
  • Fondo que sigue haciendo scroll (pierdes contexto).
  • Modal sin región de scroll clara (la gente “se queda atrapada”).

Buenas prácticas:

  • Bloquea scroll del fondo mientras está abierto (sin impedir scroll dentro).
  • Si el contenido es largo, define un área scrollable dentro del modal y asegúrate de que el foco sigue siendo visible al navegar.
  • Evita “modales infinitos”: si el contenido pasa de “microtarea” a “lectura”, probablemente no es un modal.

Checklist express de modal (para pegar en tu PR)

  • Semántica: role="dialog", aria-modal="true", aria-labelledby (y aria-describedby si aplica).
  • Foco: mover foco al abrir + restaurar al cerrar.
  • Teclado: Tab atrapado dentro; Escape cierra.
  • Salida clara: botón cerrar visible y accesible.
  • Scroll: fondo bloqueado, contenido del modal usable con zoom.

Accordions accesibles: cómo hacerlo sin romper la UX

Un accordion reduce carga visual (menos contenido en pantalla), pero puede aumentar tiempo de decisión (hay que descubrir qué hay dentro). Bien usado, es oro para contenidos secundarios; mal usado, se convierte en “contenido escondido para siempre”.

El patrón correcto (y por qué “div clickable” no vale)

En el APG, la interacción base del accordion es simple:

  • El encabezado es un control (normalmente un <button>).
  • Enter o Space expanden/colapsan.
  • Todo lo enfocables del accordion participan del orden natural de Tab (no inventes un “sub-tabindex hell”).

Ejemplo:

<h3>
  <button aria-expanded="false" aria-controls="panel-faq-1" id="acc-faq-1">
    ¿Qué incluye el plan?
  </button>
</h3>
<div id="panel-faq-1" role="region" aria-labelledby="acc-faq-1" hidden>
  <p>Incluye soporte, actualizaciones y…</p>
</div>

UX que no castiga a nadie

  • Pistas visuales claras: icono + estado (rotación/flecha) + animación suave.
  • No cambies el foco al abrir/cerrar: que el usuario mantenga el control.
  • Si permites “solo un panel abierto”, hazlo por un motivo real (en móvil puede ayudar). Si no, deja abrir varios: reduce “memoria de trabajo” (menos carga cognitiva).

Checklist del accordion

  • Encabezado con <button> + aria-expanded + aria-controls.
  • Panel asociado con id correcto (y role="region" + aria-labelledby si el contenido lo justifica).
  • Teclado: Enter y Space funcionan siempre.
  • Estado visible (no solo color; piensa en accesibilidad visual).

Tabs accesibles: el patrón ARIA bien aplicado

Las tabs pueden bajar tiempo de decisión (organizan contenido en categorías), pero también pueden subir carga cognitiva si:

  • hay demasiadas,
  • cambian el contenido de forma inesperada,
  • o están implementadas como links/botones sin patrón consistente.

El APG define claramente los roles y relaciones:

  • role="tablist" para el contenedor.
  • role="tab" para cada pestaña.
  • role="tabpanel" para el panel.
  • aria-controls desde tab → panel, y aria-labelledby desde panel → tab.

Ejemplo mínimo:

<div role="tablist" aria-label="Ajustes">
  <button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
    Perfil
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
    Seguridad
  </button>
</div>

<section role="tabpanel" id="panel-1" aria-labelledby="tab-1">
  ...
</section>
<section role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
  ...
</section>

Detalle importante: suele esperarse que solo la tab activa esté en el orden de tabulación (las demás con tabindex="-1"), y que el cambio entre tabs sea con flechas (izquierda/derecha). Este enfoque aparece en guías y análisis de soporte de a11y para tabs.

Consejos de UX para tabs (sin romper accesibilidad)

  • Máximo razonable: 4–7 tabs (más que eso suele ser un “menú disfrazado”).
  • Evita que cada tab sea una “pantalla entera” si el contenido requiere scroll largo: ahí mejor navegación real o accordion/secciones.
  • Mantén el foco en la tab (no lo saltes al panel automáticamente) salvo que el caso de uso lo pida.

Checklist de tabs

  • Roles correctos (tablist, tab, tabpanel) y relaciones (aria-controls, aria-labelledby).
  • aria-selected siempre coherente (una activa, el resto no).
  • Navegación por flechas + Home/End (si lo implementas, que sea consistente).
  • Panels ocultos realmente no enfocables (cuidado con elementos tabulables dentro).

Dropdowns vs Select: cuándo uno es un problema de accesibilidad

Aquí hay una regla que te ahorra discusiones eternas: si estás eligiendo un valor de formulario simple, usa <select> nativo. Es robusto, soporta teclado, lector de pantalla y UX móvil de forma casi perfecta.

Entonces, ¿cuándo aparece el dropdown custom (o combobox)?

El combobox (dropdown “con cerebro”) tiene un patrón… y es complejo

El APG define el combobox como un input (o botón) que controla un popup (listbox, grid, etc.).
MDN remarca que puede ser editable o “select-only” (sin texto libre), pero sigue siendo un widget compuesto.
Y el propio APG muestra ejemplos “select-only” que imitan a <select>, precisamente para casos donde necesitas comportamiento extra.

Traducción práctica: si no necesitas búsqueda, virtualización, agrupaciones ricas o async, el dropdown custom normalmente aumenta carga cognitiva (para usuario) y carga de mantenimiento (para ti). Y ahí la accesibilidad web sufre.

“Dropdown” no siempre significa lo mismo

  • Select (valor): el usuario elige un valor para un campo.
  • Menu button (acciones): el usuario elige una acción (“Duplicar”, “Archivar”, “Eliminar”).

El APG separa claramente el patrón de menu button (botón que abre un menú de acciones).
Si usas role="menu" para navegación o para seleccionar valores, es fácil terminar con un comportamiento de teclado tipo “aplicación de escritorio” que no encaja con la web. (Y sí, se nota en UX.)

Mini matriz de decisión (rápida y útil)

Usa <select> si:

  • Lista corta/mediana.
  • No necesitas búsqueda.
  • Prioridad: accesibilidad + UX móvil + simplicidad.

Usa combobox si:

  • Lista enorme (50–500+).
  • Necesitas búsqueda/filtrado.
  • Opciones asíncronas (API).
  • Necesitas mostrar metadata en cada opción (pero ojo con recargar).

Usa menu button si:

  • Son acciones, no valores.

Toasts y notificaciones: cómo anunciar cambios sin molestar

Las toasts existen para comunicar cambios sin interrumpir. WCAG lo aborda con el criterio de status messages: informar cambios relevantes que no reciben foco, sin cortar el trabajo del usuario.

¿Qué rol uso: status o alert?

  • role="status" / aria-live="polite": confirmaciones no urgentes (“Guardado”, “Añadido al carrito”).
  • role="alert" / aria-live="assertive": cosas urgentes (“Error al pagar”, “Sesión caducada”).

MDN describe role="alert" como un mensaje importante y sensible al tiempo, tratado como live region “atómica”.
Y la guía de live regions de MDN explica cuándo usar anuncios “polite” para evitar ser molestos.

Ejemplo “no invasivo”:

<div aria-live="polite" aria-atomic="true" class="sr-only" id="live-region"></div>

<!-- Visual -->
<div class="toast" role="status" aria-atomic="true">
  Guardado correctamente.
  <button aria-label="Cerrar notificación">×</button>
</div>

Reglas de oro (para no generar odio)

  • No robes foco para una toast normal.
  • Si auto-desaparece, da tiempo suficiente y ofrece cerrar manualmente.
  • No apiles 6 mensajes: agrupa (“3 cambios guardados”).
  • Si un mensaje requiere acción, probablemente no es toast, es un banner persistente o un diálogo.

Técnica relacionada: WAI describe el uso de live regions para notificar errores sin mover foco.

Carousels accesibles: la dura verdad (y alternativas mejores)

Vamos a decirlo claro: muchos carousels existen por estética, no por necesidad, y suelen empeorar accesibilidad visual y a11y (movimiento, distracción, controles pequeños, lectura fragmentada).

Dicho eso: si tu producto realmente necesita un carrusel (p. ej., galería, stories, destacados), el APG tiene un patrón específico.

El problema grande: movimiento automático

Si el carrusel se mueve solo y dura más de 5 segundos, necesitas un mecanismo para pausar, detener u ocultar. Esto está alineado con WCAG “Pause, Stop, Hide”, que busca evitar distracciones durante la interacción.

Además, hay ejemplos de carrusel auto-rotativo que detienen la rotación cuando el usuario enfoca controles o interactúa, como medida esencial de accesibilidad.

Si aun así lo implementas, que sea “control-first”

  • Botones Prev/Next grandes, con labels claros.
  • Indicadores (puntos) accesibles y operables por teclado.
  • Pausa/Reproducir visible si hay auto-rotación.
  • Respeta prefers-reduced-motion (reduce o elimina animaciones).
  • No cambies de slide si el usuario está leyendo/interactuando.

Alternativas mejores (casi siempre)

  • Grid de tarjetas con “Ver más”.
  • Lista horizontal con scroll (tipo “cards”) + botones opcionales.
  • Destacados estáticos con buena jerarquía (un hero + 3 links).

Incluso navegadores y guías modernas están empujando a carousels más “declarativos” y menos frágiles, reduciendo problemas típicos de a11y cuando se construyen con scroll en lugar de “slides” artificiales.

Preguntas frecuentes (FAQs)

1) ¿“Cumplir” ARIA significa que mi componente ya es accesible?

No. ARIA describe roles y estados, pero no arregla problemas de foco, teclado, orden lógico o anuncios de estado. Piensa ARIA como una parte del contrato, no como el producto final. El APG existe precisamente para unir semántica + comportamiento + ejemplos.

2) ¿Puedo usar librerías de componentes y olvidarme del tema?

Puedes apoyarte, pero no “olvidarte”. Una librería puede implementar el patrón (bien), pero tú sigues decidiendo: contenido, jerarquía, densidad, textos, timing de toasts, cuándo usar modal, etc. Y esas decisiones afectan directamente a carga cognitiva y tiempo de decisión.

3) ¿Cómo testeo rápido estos componentes sin montar un laboratorio?

Checklist mínima:

  • Solo teclado (Tab/Shift+Tab/Enter/Escape/flechas).
  • Zoom al 200% y 400% (¿se rompe el layout? ¿se pierde el foco?).
  • prefers-reduced-motion activado.
  • Un lector de pantalla (aunque sea básico) para comprobar: ¿se anuncia el título del modal? ¿se anuncia la toast?
    Para mensajes de estado, ten presente el objetivo de WCAG: informar cambios sin interrumpir ni mover el foco.

Accesibilidad es diseñar el “camino fácil”

Cuando un componente UI es accesible, normalmente ocurre algo bonito: baja el tiempo de decisión y baja la carga cognitiva. No porque “le pusiste ARIA”, sino porque el componente:

  • tiene un propósito claro,
  • no esconde la información importante,
  • respeta expectativas de teclado,
  • y anuncia cambios sin invadir.

La accesibilidad web, en el fondo, es ingeniería de la atención: hacer que la interfaz no le pida a la persona más memoria, más precisión o más paciencia de la necesaria. Y eso, además de a11y, es simplemente buen desarrollo web.

Links accesibles: “haz click aquí” es un crimen

Ilustración tipo banner sobre accesibilidad web: “Links accesibles: ‘haz click aquí’ es un crimen”. En una ventana de navegador aparece un enlace azul subrayado “Haz click aquí”, un icono de accesibilidad y una señal de advertencia.

Si has escrito alguna vez “haz click aquí”, tranquilo: no eres la primera persona. Lo que sí sería un crimen (con premeditación y alevosía UX) es dejarlo ahí sabiendo que un enlace es mucho más que un trocito subrayado en azul.

En accesibilidad web (a11y) y en contenido y diseño inclusivo, los enlaces son un punto crítico porque funcionan como decisiones en miniatura: cada link le pide al usuario que entienda algo, elija algo y actúe. Y ahí entra una comparación que te va a servir para auditar tu interfaz sin excusas:

  • Tiempo de decisión: cuánto tarda alguien en entender “qué hace esto” y decidir si le interesa.
  • Carga cognitiva: cuánta energía mental necesita para interpretar el texto, el contexto, el estilo visual, los estados y la promesa del enlace.

Cuando el enlace está mal redactado (“aquí”, “ver más”, “leer”, “clic”), suben ambas: tardas más en decidir y además te cansas antes. Cuando está bien, la interfaz se vuelve más rápida, más clara y más accesible.

Vamos a destriparlo con ejemplos técnicos y de interacción, sin postureo.

Por qué “haz click aquí” falla en accesibilidad (y también en UX)

“Haz click aquí” falla por tres razones muy prácticas:

  1. No describe el destino (ni la acción).
  2. No sobrevive fuera de contexto.
  3. Excluye: no todo el mundo “hace click” (móvil, teclado, voz, lector de pantalla, switch, etc.).

Cómo leen los usuarios visuales: escanean, no leen linealmente

En la vida real, mucha gente no “lee” tu página: escanea. Busca palabras-ancla (nombres, verbos, conceptos) que le permitan decidir rápido.

  • “Haz click aquí” no aporta información semántica.
  • Obliga a mirar alrededor para entender “¿aquí para qué?”.
  • Aumenta tiempo de decisión y carga cognitiva porque el usuario tiene que reconstruir significado.

Micro-ejemplo: el enlace no debe pedir contexto prestado

Mal:

  • “Para más información sobre accesibilidad visual, haz click aquí.”

Bien:

  • “Más información sobre accesibilidad visual en interfaces.”

En la versión buena, el enlace ya contiene la promesa. El ojo lo detecta y el cerebro decide sin esfuerzo.

Cómo lo leen los lectores de pantalla: listas de enlaces sin el resto del texto

Esto es clave en accesibilidad web: muchos usuarios de lectores de pantalla navegan saltando por enlaces o incluso abriendo una lista de enlaces de la página. En ese escenario, “aquí” se convierte en una colección de nada.

Imagina una lista así:

  • aquí
  • aquí
  • ver más
  • ver más
  • leer
  • leer

Eso no es navegación: es un laberinto.

Lo que queremos que pase

Queremos que, al extraer los links, sigan teniendo sentido:

  • Guía de contenido y diseño inclusivo
  • Checklist de a11y para desarrollo web
  • Descargar Informe de accesibilidad (PDF)
  • Ver ejemplos de links accesibles en UI

Aquí baja el tiempo de decisión (se entiende rápido) y baja la carga cognitiva (no hay que deducir).

Qué convierte un enlace en accesible: semántica + contexto + forma

Un enlace accesible no es solo “texto bonito”. Es la suma de:

  • Semántica: el texto describe destino/acción.
  • Contexto: se entiende incluso fuera del párrafo.
  • Interacción: se percibe como link y se puede usar con distintos métodos.
  • Diseño: contraste, foco, estados, tamaño y consistencia.

Regla de oro: el texto del enlace debe describir el destino

Piensa en el enlace como un “título de acción”. Debe responder a una de estas preguntas:

  • ¿Qué voy a ver?
  • ¿Qué voy a hacer?
  • ¿Qué se descarga y en qué formato?
  • ¿A dónde me lleva?

Plantillas que funcionan (y posicionan mejor)

En SEO y accesibilidad, suele funcionar combinar verbo + objeto o objeto + aclaración:

  • Leer: “Cómo auditar accesibilidad web en un proyecto React”
  • Ver: “Ejemplos de links accesibles en navegación”
  • Descargar: “Descargar guía de a11y (PDF, 1,2 MB)”
  • Abrir herramienta: “Abrir validador de contraste”

Fíjate que aquí no solo mejoras accesibilidad visual y cognitiva: también estás metiendo términos significativos que ayudan al posicionamiento y a la comprensión.

Evita “ver más” repetido en cards: patrón típico, problema típico

En grids de artículos o tarjetas, es habitual ver:

  • Card 1 → “Ver más”
  • Card 2 → “Ver más”
  • Card 3 → “Ver más”

Para un lector de pantalla, eso es casi inútil. Para un usuario visual, obliga a fijarse en el título de la tarjeta, generando micro-fricción repetida.

Solución 1: convierte el título en el enlace principal

Recomendación: el título del contenido suele ser el mejor texto de enlace.

<article>
  <h3>
    <a href="/blog/links-accesibles">Links accesibles: “haz click aquí” es un crimen</a>
  </h3>
  <p>Cómo reducir carga cognitiva y mejorar a11y con microcopy.</p>
</article>

Solución 2: si necesitas un CTA extra, hazlo específico

  • “Leer el artículo sobre a11y y enlaces”
  • “Ver ejemplos de contenido inclusivo”

Si por diseño quieres mantener un “Ver más”, entonces enriquece el nombre accesible:

<a href="/blog/links-accesibles" aria-label="Ver más: Links accesibles, haz click aquí es un crimen">
  Ver más
</a>

Ojo: aria-label es un parche útil, pero lo ideal es que el texto visible ya sea bueno. Si la UI es ambigua, el problema no se arregla solo con ARIA.

Botón vs enlace: no es estética, es semántica (y comportamiento)

Un error frecuente en desarrollo web: usar <a> para acciones y <button> para navegación, o al revés. Esto afecta accesibilidad, expectativas y hasta analítica.

Cuándo usar enlace

Usa <a> cuando el objetivo sea navegar:

  • Ir a otra URL
  • Abrir una sección (hash)
  • Ir a un recurso externo

Cuándo usar botón

Usa <button> cuando el objetivo sea ejecutar una acción:

  • Abrir un modal
  • Enviar un formulario
  • Filtrar resultados (sin cambiar URL)
  • Expandir/contraer contenido

Ejemplo de patrón correcto en filtros

Bien:

  • Botones para filtrar
  • Enlaces para navegar a detalle

Esto reduce carga cognitiva: el usuario no tiene que adivinar si “parece link” pero actúa como botón.

Interacción accesible: foco, estados, contraste y área clicable

Un enlace accesible también se “lee” con los dedos y con el teclado.

Foco visible: si navegas con teclado, esto es tu mapa

Si el foco no se ve, estás quitando navegación a una parte de usuarios (y generando frustración). La accesibilidad visual aquí es directa.

Recomendaciones:

  • Que el foco sea muy evidente (no un cambio sutil de color).
  • Que haya estilos en :focus-visible.
  • Que no dependan solo del color.

Ejemplo en Tailwind (práctico y limpio)

<a
  href="/guia-a11y"
  class="underline underline-offset-4 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-4"
>
  Guía de accesibilidad web (a11y)
</a>

No uses solo color para indicar “esto es un link”

Si tu enlace se distingue solo por color, estás cargando la interfaz de ambigüedad para:

  • Personas con baja visión
  • Daltonismo
  • Pantallas con bajo contraste
  • Contextos con brillo alto

La solución clásica y efectiva: subrayado (sí, subrayado). Si lo quitas por estética, entonces debes reemplazarlo por un patrón igual de robusto, no por “ya se intuía”.

Tamaño del objetivo: enlaces pequeños, precisión imposible

En móvil, un link minúsculo es un castigo. Y en accesibilidad, un objetivo pequeño aumenta errores, reintentos y fatiga (hola, carga cognitiva).

Hazlo fácil de tocar:

  • Más padding
  • Más separación entre links
  • Evita “links pegados” en un párrafo sin espacios

Enlaces especiales: nueva pestaña, descargas, externos e iconos

Aquí es donde muchos proyectos se rompen: el usuario hace click y pasan cosas que no esperaba.

Si abre en nueva pestaña, dilo (o al menos indícalo)

Abrir en nueva pestaña sin avisar puede desorientar. Si lo haces, sé transparente:

  • “Abrir documentación (se abre en una pestaña nueva)”
  • Icono + texto oculto accesible
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
  Documentación externa <span class="sr-only">(se abre en una pestaña nueva)</span>
</a>

Si es una descarga, indica formato y peso cuando sea relevante

Esto es contenido inclusivo: ayudas a decidir antes de actuar.

  • “Descargar informe de accesibilidad (PDF, 2 MB)”
  • “Descargar dataset (CSV, 450 KB)”

Baja el tiempo de decisión y evita sorpresas (baja carga cognitiva y frustración).

Icon-only links: siempre con nombre accesible

Si tienes un icono de GitHub, Twitter o “compartir”, recuerda: el icono no siempre “se lee”.

<a href="https://github.com/tuusuario" aria-label="Ir a GitHub de Marta González">
  <!-- svg -->
</a>

Mejor aún: combina icono + texto visible cuando la UI lo permita.

Ejemplos “antes y después” para que puedas aplicarlo hoy

En un párrafo informativo

Antes:

“Si quieres aprender sobre accesibilidad web, haz click aquí.”

Después:

“Aprende sobre accesibilidad web y a11y en desarrollo web con esta guía práctica.”

En una lista de recursos

Antes:

  • Click aquí
  • Click aquí
  • Click aquí

Después:

  • Checklist de accesibilidad visual para interfaces
  • Guía de contenido y diseño inclusivo
  • Buenas prácticas de enlaces accesibles en React

En cards de blog

Antes:

  • Título
  • Descripción
  • “Ver más”

Después (mejor patrón):

  • Título (enlace)
  • Descripción
  • Opcional: “Leer el artículo”

En 2 minutos, revisa esto

  • ¿Cada enlace tiene sentido fuera de contexto?
  • ¿Hay “aquí / ver más / leer” repetidos?
  • ¿El texto describe destino o acción?
  • ¿El foco es visible y claro?
  • ¿El link se distingue sin depender solo del color?
  • ¿Los iconos tienen nombre accesible?
  • ¿Se avisa si abre nueva pestaña o descarga?
  • ¿Link y botón están bien usados semánticamente?

Mini-test mental: “lista de enlaces”

Si un usuario escuchara solo tus enlaces, ¿podría navegar sin perderse? Si la respuesta es “no”, ya sabes por dónde empezar.

Preguntas frecuentes (FAQs)

1) ¿Puedo usar “aquí” si el texto alrededor explica el destino?

Poder, puedes. Pero no es buena práctica. En accesibilidad web, el enlace debe ser autosuficiente porque puede ser consumido fuera del contexto (listas de enlaces, escaneo, navegación por teclado). Si quieres mantener un tono natural, integra el enlace en una frase descriptiva.

2) ¿Qué hago si por diseño necesito que el CTA sea corto?

Haz el texto corto, pero significativo. “Ver guía” es mejor que “Ver”. Y si aún así necesitas un genérico por consistencia visual, complementa con un nombre accesible (por ejemplo, aria-label). Aun así, lo recomendable es que el texto visible haga el trabajo principal.

3) ¿Es mejor enlazar una card completa o solo el título?

Depende, pero cuidado: una card entera clicable puede crear problemas de accesibilidad si no está bien implementada (teclado, foco, elementos interactivos anidados). El patrón más robusto suele ser: título enlazado + CTA secundario opcional, evitando zonas “misteriosamente clicables”.

Un enlace es una promesa (y también un acto de respeto)

Un link no es un adorno: es una promesa de navegación y una petición de atención. Cuando escribes “haz click aquí”, estás delegando el significado en el contexto y empujando al usuario a hacer trabajo extra. Eso sube el tiempo de decisión y la carga cognitiva, y en accesibilidad eso se traduce en barreras reales.

En cambio, cuando un enlace es claro, descriptivo y coherente con la interacción, ocurre algo muy potente: la interfaz se vuelve más rápida para quien escanea, más fiable para quien navega con teclado, más comprensible para quien usa lector de pantalla y más amable para quien simplemente está cansado y quiere que la web deje de ponerle trampas.

Si hoy te llevas una sola regla, que sea esta: el texto del enlace debe poder vivir solo. Si puede, tu UI respira. Si no puede, estás a un “aquí” de convertir la navegación en un crimen.

HTML semántico: el 80% de la accesibilidad empieza aquí

Si tuvieras que apostar por una sola mejora para subir el nivel de accesibilidad de una web sin meterte todavía en ARIA, JavaScript complejo o auditorías eternas, sería esta: usar HTML semántico de verdad.

Y no por romanticismo “old school”, sino por algo muy práctico: la semántica reduce el trabajo de interpretación que tienen que hacer tanto las personas como las tecnologías de asistencia (lectores de pantalla, navegación por teclado, control por voz, etc.). Dicho de otra forma: mejora el tiempo de decisión (la gente entiende antes qué está pasando) y baja la carga cognitiva (hay menos ambigüedad y menos esfuerzo mental para navegar y actuar).

En este artículo vamos a construir una idea clara: la accesibilidad no empieza en ARIA; empieza en el marcado. Y sí, vas a ver ejemplos aplicables, patrones de interacción típicos (menús, modales, acordeones, formularios) y una checklist para aterrizarlo en proyectos reales.

Por qué la semántica “hace accesible” antes que cualquier otra cosa

La accesibilidad web no es un “extra” que se añade al final como una capa. Se construye desde el principio con decisiones pequeñas y constantes: estructura, nombres, jerarquías, foco, estados y mensajes de feedback. Por eso el HTML semántico juega con ventaja: no solo “pinta” contenido, declara qué es cada cosa y para qué sirve; es decir, codifica la intención.

Cuando escribes:

  • <button> estás declarando: “esto es accionable y se puede activar con teclado”.
  • <a href="..."> estás diciendo: “esto es navegación”.
  • <h2> estás marcando: “este bloque tiene un tema y está en una jerarquía”.
  • <main> estás señalando: “aquí está el contenido principal”.

Eso permite que el navegador, el lector de pantalla y el usuario formen un modelo mental consistente. Menos dudas, menos ensayo-error, menos “¿dónde estoy?”.

Tiempo de decisión vs. carga cognitiva (la comparación clave)

  • Tiempo de decisión: cuánto tardas en decidir “qué puedo hacer aquí” y “dónde debo ir”.
  • Carga cognitiva: cuánta energía mental te cuesta mantenerte orientada, entender la estructura, recordar opciones y estados.

El HTML semántico reduce ambos porque:

  1. Acelera el reconocimiento (lo que ves/oyes coincide con lo esperado).
  2. Da pistas estructurales (landmarks, headings, listas, regiones).
  3. Evita ambigüedades (no todo es un <div> clicable).

Landmarks: el mapa de una web para lectores de pantalla (y para ti)

Los landmarks son regiones semánticas que permiten saltar por la página como si tuvieras un índice. Son esenciales para navegar rápido, sobre todo cuando no usas ratón o cuando la página es larga.

Los landmarks básicos que deberías usar siempre

  • <header>: cabecera de la página o de una sección.
  • <nav>: navegación (principal o secundaria).
  • <main>: contenido principal (debe haber solo uno por página).
  • <aside>: contenido complementario (relacionado, pero no central).
  • <footer>: pie de página o de sección.
  • <section> / <article>: para agrupar contenido con sentido (y no “porque sí”).

Regla útil: si una persona no puede ponerle un título a ese bloque, probablemente no es una <section>.

Ejemplo recomendado de estructura (con “skip link”)

El skip link es un enlace al principio que permite saltar el menú y aterrizar en el contenido. Es pequeñísimo… y cambia la vida cuando navegas con teclado.

<a class="skip-link" href="#contenido">Saltar al contenido</a>

<header>
  <nav aria-label="Navegación principal">
    <ul>
      <li><a href="/blog">Blog</a></li>
      <li><a href="/proyectos">Proyectos</a></li>
      <li><a href="/contacto">Contacto</a></li>
    </ul>
  </nav>
</header>

<main id="contenido">
  <h1>HTML semántico: el 80% de la accesibilidad empieza aquí</h1>
  ...
</main>

<footer>
  <p>© 2026</p>
</footer>

Qué ganas aquí:

  • Navegación rápida por regiones.
  • Menos tabulaciones para llegar al contenido.
  • Mejor comprensión para lectores de pantalla.
  • Mejor SEO (porque la estructura tiene significado).

Microdetalle que marca diferencia: aria-label en <nav>

Si tienes más de una navegación (header, footer, lateral), nómbralas:

  • aria-label="Navegación principal"
  • aria-label="Navegación del pie"
  • aria-label="Navegación secundaria"

Eso baja la carga cognitiva: la persona no tiene que adivinar cuál es cuál.

Headings: tu jerarquía es la tabla de contenidos de la web

Si hay un “pecado mortal” frecuente es usar headings para tamaño visual en lugar de estructura lógica. Un <h2> no es “texto grande”; es una relación jerárquica con el resto del documento.

Reglas prácticas (sin volverte loca)

  • Debe haber un solo <h1> que represente el tema principal de la página.
  • Los <h2> abren secciones principales.
  • Los <h3> son subsecciones dentro de un <h2>.
  • No saltes niveles “porque queda bonito” (de <h2> a <h4>), salvo casos muy justificados.

Impacto directo en tiempo de decisión:
Muchísima gente navega “escaneando” headings. Si la jerarquía está bien, la persona tarda menos en decidir dónde está lo importante.

Truco de diseño e interacción: “headings como anclas de navegación”

Si tu artículo es largo, crea una mini tabla de contenidos con links a headings. No es solo SEO: es accesibilidad y usabilidad.

<nav aria-label="Contenido del artículo">
  <ol>
    <li><a href="#landmarks">Landmarks</a></li>
    <li><a href="#headings">Headings</a></li>
    <li><a href="#formularios">Formularios</a></li>
  </ol>
</nav>

Componentes interactivos: si no es un <button>, probablemente está mal

Aquí es donde más accesibilidad se rompe en proyectos modernos: divs clicables, spans con onClick, cards enteras que “parecen botones”, menús desplegables sin foco, etc.

Botón vs enlace (la decisión que te ahorra bugs)

  • Enlace (<a href>): navega a otro lugar.
  • Botón (<button>): ejecuta una acción en la misma vista.

Ejemplos:

✅ Correcto:

<a href="/checkout">Ir a checkout</a>
<button type="button" aria-expanded="false">Mostrar filtros</button>

❌ Problemático:

<div onclick="toggleFilters()">Mostrar filtros</div>
<span onclick="goCheckout()">Checkout</span>

Por qué es grave: un <div> no es activable por teclado por defecto, no tiene rol correcto, no tiene estados, no se anuncia como control. Estás aumentando carga cognitiva y empeorando el tiempo de decisión: la persona no sabe si eso es interactivo hasta probar.

Acordeón accesible (semántica primero)

Si puedes resolverlo con <details> y <summary>, hazlo. Es semántico, nativo y bastante accesible sin inventar la rueda.

<details>
  <summary>¿Qué incluye el patrón?</summary>
  <p>Materiales, abreviaturas, paso a paso y consejos.</p>
</details>

Si necesitas un acordeón custom (por diseño), al menos respeta: botón, aria-expanded, panel asociado, foco coherente.

Formularios: la accesibilidad se juega en los detalles

Los formularios son el lugar donde el “solo se ve bien” más rápido se convierte en frustración. Aquí la semántica es tu mejor aliada.

Labels reales (no “placeholders que hacen de label”)

El placeholder no sustituye un <label>. Se pierde al escribir, tiene contraste pobre y no se anuncia igual.

✅ Correcto:

<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" />

Fieldset y legend: oro para grupos de opciones

Si tienes radios/checkboxes relacionados, agrupa:

<fieldset>
  <legend>Preferencias de contacto</legend>
  <label><input type="radio" name="contacto" value="email" /> Email</label>
  <label><input type="radio" name="contacto" value="telefono" /> Teléfono</label>
</fieldset>

Esto reduce carga cognitiva porque el grupo tiene un contexto explícito.

Errores claros, asociados y recuperables

Cuando hay error, la persona debe:

  1. entender qué pasó,
  2. saber cómo arreglarlo,
  3. recuperar sin perderse.

Ejemplo:

<label for="nombre">Nombre</label>
<input id="nombre" aria-describedby="err-nombre" />
<p id="err-nombre">Tu nombre es obligatorio.</p>

Consejo de interacción: al enviar, mueve foco al primer error o a un resumen de errores arriba. Eso reduce muchísimo el tiempo de decisión.

ARIA: úsalo como bisturí, no como maquillaje

ARIA es útil, pero tiene una regla no escrita que te salva: si existe un elemento HTML nativo que lo resuelve, úsalo. El HTML semántico suele ser más robusto, más compatible y menos propenso a bugs.

Casos típicos donde ARIA se usa mal

  • Añadir role="button" a un <div> en lugar de usar <button>.
  • Añadir aria-label para “arreglar” textos que deberían ser visibles.
  • Inventar roles en elementos que ya tienen semántica.

Resultado: sube la complejidad, sube la carga cognitiva para quien mantiene el código, y no necesariamente mejora la experiencia real.

Checklist rápida para revisar tu HTML semántico en 15 minutos

  1. ¿Hay un <main> único por página?
  2. ¿Hay un <h1> único y headings sin saltos raros?
  3. ¿Las zonas clave están en landmarks (header/nav/main/footer/aside)?
  4. ¿Los controles interactivos son botones/enlaces reales (no divs)?
  5. ¿Los formularios tienen <label> y mensajes de error asociados?
  6. ¿Existe “skip link” para saltar al contenido?
  7. ¿La navegación tiene nombres si hay más de un <nav>?
  8. ¿Las listas son listas (<ul>/<ol>) y no “párrafos con guiones”?

Preguntas frecuentes (FAQs)

1) ¿De verdad “el 80%” de la accesibilidad depende del HTML semántico?

No es un porcentaje científico, pero sí una forma útil de priorizar: con buena semántica resuelves gran parte de navegación, comprensión, foco por defecto y compatibilidad con tecnologías de asistencia. Luego vienen capas: CSS (contraste), JS (gestión de foco), contenido (lenguaje claro), y casos específicos donde ARIA ayuda.

2) ¿Puedo hacer una web accesible solo con ARIA aunque use muchos <div>?

Puedes mejorar algo, pero es como poner señalización en un edificio mal construido. ARIA añade información, sí, pero no reemplaza comportamientos nativos (teclado, estados, foco, activación). En la práctica, mantenerlo es más frágil y sube la carga cognitiva del equipo.

3) ¿Qué es lo primero que reviso si tengo poco tiempo?

Empieza por lo que más impacta el tiempo de decisión: headings + landmarks + controles correctos. Si la estructura se entiende y se navega bien, ya has ganado mucho. Luego entra a formularios y feedback de errores.


Accesibilidad es diseñar para decidir rápido y sin esfuerzo

Cuando hablamos de accesibilidad, a veces se piensa en “cumplir requisitos”. Pero el marco más potente es otro: diseñar para reducir incertidumbre.

Un HTML semántico bien hecho:

  • ayuda a que la persona entienda dónde está,
  • le deja moverse con confianza,
  • y le permite actuar sin fricción.

Eso es exactamente el equilibrio entre tiempo de decisión y carga cognitiva: cuanto menos tengas que “descifrar” una interfaz, más energía te queda para lo importante (leer, comprar, aprender, crear, contactar, disfrutar).

Así que la próxima vez que estés a punto de escribir un <div> con onClick, párate un segundo y pregúntate: ¿qué es esto, de verdad? Si lo nombras bien en HTML, ya estás haciendo accesible tu producto antes de escribir la primera línea de ARIA.