Criterios de éxito de las WCAG · Level A

WCAG 2.1.2: Sin trampa de teclado

WCAG 2.1.2 requiere que las personas que usan el teclado nunca queden atrapadas dentro de un componente: si el foco se puede mover a un elemento de la interfaz de usuario usando el teclado, también debe ser posible mover el foco fuera usando solo el teclado. Este criterio es esencial para las personas que dependen exclusivamente de la navegación por teclado, incluidas las personas con discapacidades motoras y las usuarias y usuarios de lectores de pantalla.

Qué Significa Esta Regla

WCAG 2.1.2 — Sin trampa de teclado — es un criterio de éxito de Nivel A bajo el principio de Operable. Establece: «Si el foco del teclado se puede mover a un componente de la página usando una interfaz de teclado, entonces el foco se puede mover fuera de ese componente usando solo una interfaz de teclado y, si se requiere más que teclas de flecha o teclas de tabulación sin modificar para mover el foco fuera, se informa al usuario del método para mover el foco».

En términos prácticos, esto significa que cada componente interactivo en una página web al que una persona usuaria de teclado pueda llegar con la tecla Tab también debe permitir que la persona pueda salir de él con Tab. Se produce una trampa de teclado cuando una persona navega hasta un widget, un cuadro de diálogo, un control de formulario personalizado, un reproductor multimedia incrustado o cualquier otra región focalizable y luego no puede salir usando los controles estándar del teclado: en efecto, se queda atrapada.

El criterio abarca todos los elementos HTML que pueden recibir el foco del teclado: elementos interactivos nativos como las etiquetas <input>, <select>, <textarea>, <button> y <a>, así como widgets personalizados que reciben el foco mediante tabindex, roles ARIA o gestión del foco con JavaScript. Se aplica por igual a componentes incrustados de terceros como widgets de chat, reproductores de video, mapas incrustados, selectores de fecha y editores de texto enriquecido.

Un componente cumple este criterio si una persona usuaria de teclado siempre puede mover el foco fuera de él usando teclas estándar — normalmente Tab, Shift+Tab, Escape o teclas de flecha — o si la página informa claramente a las personas usuarias de un mecanismo de teclado no estándar para mover el foco fuera. Un componente no cumple si el foco queda bloqueado dentro de él sin una vía de salida accesible por teclado, o si el único mecanismo de salida requiere un clic de ratón, un gesto táctil u otra entrada que no sea de teclado.

WCAG sí proporciona una excepción limitada: es aceptable restringir el foco del teclado dentro de un componente temporalmente cuando hacerlo forma parte de un patrón de diseño deliberado y accesible — sobre todo, un cuadro de diálogo modal. Un cuadro de diálogo modal debe atrapar el foco dentro del diálogo mientras está abierto (para evitar que las personas usuarias interactúen con contenido de fondo oculto), pero siempre debe proporcionar un medio accesible por teclado para cerrar el diálogo y liberar el foco, como un manejador de la tecla Escape o un botón de cierre claramente etiquetado al que se pueda llegar con Tab. Este tipo de contención del foco intencional y escapable no es una trampa de teclado; es una implementación correcta del patrón de cuadro de diálogo modal. La trampa solo se convierte en un fallo cuando la persona usuaria no puede escapar en absoluto.

Por Qué Importa

Las trampas de teclado son uno de los fallos de accesibilidad más graves que puede tener un sitio web. A diferencia de muchos otros problemas de accesibilidad que degradan la experiencia para ciertas personas usuarias, una trampa de teclado puede bloquear por completo a una persona usuaria para que no pueda seguir usando una página. Es esencialmente el equivalente a colocar una barrera física en un pasillo y no proporcionar ninguna forma de rodearla.

Los grupos más gravemente afectados son las personas con discapacidades motoras o físicas que no pueden usar un ratón y dependen totalmente de la navegación por teclado. Esto incluye a personas con afecciones como lesiones por esfuerzo repetitivo, esclerosis múltiple, enfermedad de Parkinson, cuadriplejia y parálisis cerebral. Según la Organización Mundial de la Salud, aproximadamente 1.3 mil millones de personas — el 16% de la población mundial — viven con alguna forma de discapacidad significativa, y las discapacidades motoras representan una parte sustancial de esa cifra. Para estas personas, encontrarse con una trampa de teclado en una página de pago, un formulario de inicio de sesión o un widget de chat de atención al cliente puede significar que no puedan completar la tarea en absoluto.

Las personas usuarias de lectores de pantalla — principalmente personas ciegas y con baja visión — también se ven muy afectadas. Lectores de pantalla como NVDA, JAWS y VoiceOver navegan completamente mediante comandos de teclado. Si el foco queda atrapado en un componente, la persona usuaria del lector de pantalla escucha el mismo elemento repetido cada vez que pulsa Tab, sin posibilidad de avanzar o retroceder por la página. Esto es profundamente desorientador y obliga a la persona usuaria a cerrar la pestaña del navegador o actualizar la página, perdiendo cualquier progreso que hubiera realizado.

Considere este escenario real: una persona con movilidad limitada en las manos visita un sitio de comercio electrónico turco para comprar un producto. Añade un artículo a su carrito y procede al pago. La página de pago incluye un widget de autocompletado de dirección de terceros construido con un framework de JavaScript personalizado. El widget abre una lista desplegable de sugerencias cuando el campo de dirección recibe el foco. La persona desarrolladora olvidó añadir la gestión de eventos de teclado para cerrar este desplegable, de modo que, una vez que la persona usuaria llega al campo de dirección con Tab y aparece el desplegable, no puede salir con Tab, no puede pulsar Escape y no puede cerrar el desplegable sin un clic de ratón. La persona usuaria queda completamente bloqueada para completar la compra. Esto no es una molestia menor: es una exclusión total del servicio.

Más allá del acceso para personas con discapacidad, las trampas de teclado también afectan a personas usuarias avanzadas que prefieren la navegación por teclado por rapidez, a personas usuarias en entornos empresariales donde el uso del ratón está restringido y a personas usuarias en ciertos dispositivos móviles o híbridos con teclados físicos. Corregir las trampas de teclado mejora por tanto la experiencia para un público más amplio que solo las personas con discapacidad.

Reglas Relacionadas de Axe-core

WCAG 2.1.2 requiere pruebas manuales. Ninguna regla automatizada de axe-core detecta de forma directa y fiable las trampas de teclado. Esto se debe a que las herramientas automatizadas funcionan analizando la estructura estática del DOM: pueden identificar elementos focalizables, comprobar el orden de tabulación y validar atributos ARIA, pero no pueden simular la experiencia completa de navegación interactiva con teclado que realiza una persona evaluadora. Una trampa de teclado suele surgir de la lógica de gestión de eventos de JavaScript que intercepta o suprime eventos de teclado en tiempo de ejecución; este comportamiento no es visible en la estructura del DOM y no puede inferirse mediante un analizador estático.

  • Se requieren pruebas manuales — no hay una regla específica de axe-core: Axe-core no incluye una regla que detecte automáticamente trampas de teclado porque el modo de fallo es fundamentalmente conductual. La trampa solo se manifiesta cuando una persona usuaria realmente navega hasta un componente con la tecla Tab e intenta salir. Un escáner automatizado no puede replicar esto porque tendría que simular la navegación secuencial con teclado a través de cada elemento focalizable de la página, activar todos los listeners de eventos de JavaScript asociados y luego determinar si el foco ha salido de la región prevista, un problema que es computacionalmente intratable para páginas complejas. Además, lo que constituye una trampa frente a una contención de foco intencional (como un modal) requiere un juicio contextual que las reglas automatizadas no pueden realizar de forma fiable. Las personas evaluadoras deben usar axe DevTools o Lighthouse para identificar elementos focalizables y problemas de orden de tabulación como punto de partida, pero luego deben navegar manualmente con el teclado por cada región interactiva para confirmar que no existan trampas.

Cómo Probar

  1. Ejecute un escaneo automatizado de accesibilidad como línea base. Abra axe DevTools (extensión del navegador) o ejecute una auditoría de accesibilidad de Lighthouse en Chrome DevTools. Aunque ninguna de estas herramientas marcará directamente una trampa de teclado, el escaneo identificará elementos focalizables, roles ARIA faltantes y un orden de tabulación incorrecto que pueden indicar componentes interactivos de riesgo que vale la pena investigar manualmente. Tome nota de cualquier widget personalizado, iframe incrustado, componente de terceros, cuadro de diálogo modal, menú desplegable, selector de fecha, carrusel y editor de texto enriquecido: estas son las fuentes más comunes de trampas de teclado.
  2. Desconecte o aparte su ratón. Para que las pruebas manuales con teclado sean válidas, no debe usar un ratón. Coloque el ratón fuera de su alcance o desactívelo en la configuración de su sistema operativo para evitar depender de él accidentalmente durante las pruebas.
  3. Navegue por toda la página usando solo las teclas Tab y Shift+Tab. Empezando desde la barra de direcciones del navegador o la parte superior de la página, pulse Tab repetidamente y observe adónde se mueve el foco. Siga visualmente el indicador de foco en cada paso. Cuando llegue a cada componente interactivo — especialmente cualquier widget personalizado, contenido incrustado o elemento de interfaz complejo — verifique que al pulsar Tab o Shift+Tab el foco salga limpiamente del componente hacia el elemento focalizable siguiente o anterior de la página.
  4. Pruebe cada componente interactivo individualmente. Para cuadros de diálogo modales: abra el modal, verifique que el foco se mueve dentro de él, luego pulse Escape y confirme que el foco vuelve al elemento que lo activó. Para menús desplegables: abra el desplegable, navegue dentro de él, luego pulse Escape y confirme que el desplegable se cierra y el foco vuelve al disparador. Para selectores de fecha, selectores de color y widgets similares: llegue con Tab, interactúe y luego salga con Tab y confirme que el foco se va. Para iframes incrustados (por ejemplo, mapas, reproductores de video, widgets de chat): llegue con Tab al iframe, navegue dentro de él y confirme que puede salir de él con Tab de vuelta a la página principal.
  5. Pruebe con NVDA y Firefox. Abra NVDA, navegue a la página en Firefox y use Tab para moverse por los elementos interactivos. Preste atención a si NVDA lee un elemento nuevo cada vez que pulsa Tab o si parece volver al mismo elemento. Si Tab no avanza el foco más allá de un componente, se trata de una trampa de teclado.
  6. Pruebe con VoiceOver y Safari en macOS. Active VoiceOver (Command + F5), abra la página en Safari y use la tecla Tab para navegar. Confirme que VoiceOver anuncia un elemento nuevo en cada pulsación de Tab y que el foco nunca queda atrapado en una región de la que no pueda salir.
  7. Pruebe con JAWS y Chrome. Abra JAWS, navegue por la página en Chrome y use Tab para recorrer todos los componentes interactivos. Pruebe específicamente cualquier componente que use gestión del foco impulsada por JavaScript, ya que JAWS interactúa con el árbol de accesibilidad de formas que pueden revelar trampas que no son visibles solo con pruebas visuales.
  8. Compruebe si existen mecanismos de escape no estándar. Si algún componente requiere una tecla distinta de Tab, Shift+Tab o Escape para salir, verifique que la página comunique esto claramente a la persona usuaria, por ejemplo, mediante instrucciones en pantalla, un tooltip o un anuncio de una región ARIA en vivo cuando el foco entra en el componente.

Cómo Corregir

Cuadro de Diálogo Modal Sin Gestión de Escape — Incorrecto

<!-- Modal opens but has no Escape key handler and no close button reachable by keyboard -->
<div id='modal' role='dialog' aria-modal='true' aria-labelledby='modal-title'>
  <h2 id='modal-title'>Confirm Your Order</h2>
  <p>Are you sure you want to place this order?</p>
  <button onclick='submitOrder()'>Confirm</button>
  <!-- No close button, no Escape key listener -- keyboard users are trapped -->
</div>

Cuadro de Diálogo Modal Sin Gestión de Escape — Correcto

<!-- Modal with proper focus trap, Escape handler, and accessible close button -->
<div id='modal' role='dialog' aria-modal='true' aria-labelledby='modal-title'>
  <h2 id='modal-title'>Confirm Your Order</h2>
  <p>Are you sure you want to place this order?</p>
  <button onclick='submitOrder()'>Confirm</button>
  <!-- Close button is reachable by Tab and allows keyboard users to exit -->
  <button id='modal-close' onclick='closeModal()' aria-label='Close dialog'>Cancel</button>
</div>

<script>
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape') {
      closeModal(); // Escape key closes the modal and returns focus to trigger
    }
  });

  function closeModal() {
    document.getElementById('modal').hidden = true;
    document.getElementById('modal-trigger').focus(); // Return focus to opener
  }
</script>

Menú Desplegable Personalizado que Captura Todos los Eventos de la Tecla Tab — Incorrecto

<!-- Custom dropdown intercepts all keydown events including Tab, preventing focus from leaving -->
<div id='custom-select' tabindex='0' role='combobox' aria-expanded='true'>
  <ul role='listbox'>
    <li role='option' tabindex='-1'>Option 1</li>
    <li role='option' tabindex='-1'>Option 2</li>
    <li role='option' tabindex='-1'>Option 3</li>
  </ul>
</div>

<script>
  // BUG: This captures ALL keyboard events and calls preventDefault on Tab,
  // meaning the user can never Tab out of this component
  document.getElementById('custom-select').addEventListener('keydown', function(e) {
    e.preventDefault(); // This traps the keyboard
    if (e.key === 'ArrowDown') { /* move focus down */ }
    if (e.key === 'ArrowUp') { /* move focus up */ }
  });
</script>

Menú Desplegable Personalizado que Captura Todos los Eventos de la Tecla Tab — Correcto

<!-- Correct: Only prevent default for arrow keys used for internal navigation;
     allow Tab and Escape to function normally so users can exit -->
<div id='custom-select' tabindex='0' role='combobox' aria-expanded='false'>
  <ul role='listbox' hidden>
    <li role='option' tabindex='-1'>Option 1</li>
    <li role='option' tabindex='-1'>Option 2</li>
    <li role='option' tabindex='-1'>Option 3</li>
  </ul>
</div>

<script>
  document.getElementById('custom-select').addEventListener('keydown', function(e) {
    if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
      e.preventDefault(); // Only prevent default for internal navigation keys
      // move focus between options
    }
    if (e.key === 'Escape' || e.key === 'Tab') {
      // Do NOT call preventDefault -- allow Tab and Escape to propagate
      // so the browser can move focus away from this component naturally
      closeDropdown();
    }
  });
</script>

Iframe de Terceros Incrustado Sin Vía de Salida — Incorrecto

<!-- An embedded chat widget iframe that absorbs all Tab keypresses
     and never returns focus to the parent page -->
<iframe
  src='https://example-chat-widget.com/embed'
  title='Customer support chat'
  width='350'
  height='500'>
</iframe>
<!-- The iframe's internal JavaScript consumes Tab events, trapping users inside it -->

Iframe de Terceros Incrustado Sin Vía de Salida — Correcto

<!-- Use a button to open the chat in a new window rather than embedding
     an iframe that may trap keyboard users -->
<button
  id='open-chat'
  onclick='window.open("https://example-chat-widget.com", "_blank", "noopener")'>
  Open Customer Support Chat (opens in new window)
</button>

<!-- If an iframe must be used, add a visible skip link before it
     so keyboard users can bypass it if they choose -->
<a href='#after-chat-widget' class='skip-link'>Skip chat widget</a>
<iframe
  src='https://example-chat-widget.com/embed'
  title='Customer support chat'
  width='350'
  height='500'>
</iframe>
<span id='after-chat-widget' tabindex='-1'></span>

Errores Comunes

  • Llamar a event.preventDefault() dentro de un listener de keydown sin limitarlo a teclas específicas: aplicar preventDefault() a todos los eventos de teclado en un componente focalizable bloquea el funcionamiento de Tab y Shift+Tab, creando instantáneamente una trampa de teclado. Limite siempre preventDefault() solo a las teclas específicas que su componente necesita gestionar internamente (como las teclas de flecha para listboxes).
  • Construir un cuadro de diálogo modal que establece el foco dentro de él pero no proporciona un manejador de la tecla Escape ni un botón de cierre: las personas desarrolladoras a menudo implementan correctamente la parte de entrada de foco del patrón modal, pero olvidan que la contención del foco dentro de un modal solo es aceptable si siempre hay una salida accesible por teclado. Cada modal debe gestionar la tecla Escape e incluir un botón de cierre al que se pueda llegar con Tab.
  • Usar tabindex='-1' en un elemento contenedor para evitar que aparezca en el orden de tabulación, mientras se permite que JavaScript mueva el foco a él de forma programática: cuando el foco se mueve a un elemento con tabindex='-1' mediante element.focus(), no hay un destino natural de Tab para salir de él, lo que puede dejar a la persona usuaria varada si no se implementa una gestión adicional del teclado.
  • Incrustar widgets de terceros (chat, mapas, video) sin auditarlos en busca de comportamiento de trampa de teclado: las personas proveedoras no siempre construyen sus widgets incrustados para que sean accesibles por teclado. Las personas propietarias del sitio siguen siendo responsables de todo el contenido de sus páginas, incluidos los elementos incrustados de terceros. Pruebe siempre los componentes incrustados manualmente y, si atrapan a las personas usuarias de teclado, envuélvalos con un enlace de salto o sustitúyalos por una alternativa segura para el teclado.
  • Implementar una trampa de foco para un modal o panel lateral pero no liberar la trampa cuando el componente se cierra: si el JavaScript que restringe el foco no se limpia correctamente cuando el modal se cierra, puede seguir interceptando eventos de Tab y atrapar a la persona usuaria en la capa ahora invisible.
  • Usar CSS visibility: hidden o display: none para ocultar el botón de cierre de un cuadro de diálogo por motivos de diseño visual sin proporcionar una salida alternativa por teclado: si el botón de cierre está oculto visualmente pero no se elimina del árbol de accesibilidad, las personas usuarias de lectores de pantalla aún pueden encontrarlo; pero si también se oculta del árbol de accesibilidad, puede que no haya salida. Confirme que todos los mecanismos de cierre sean operables con teclado incluso si están diseñados visualmente para ser discretos.
  • Construir componentes personalizados de autocompletado o sugerencias mientras se escribe que abren una lista de sugerencias y luego redirigen todas las pulsaciones de Tab a recorrer las sugerencias en lugar de salir: las personas usuarias deben poder pulsar Escape para cerrar la lista de sugerencias y luego Tab para pasar al siguiente campo del formulario. Los widgets de autocompletado que redirigen Tab a la navegación interna incumplen este criterio.
  • Olvidar probar editores de texto enriquecido (editores WYSIWYG como TinyMCE, CKEditor o Quill): estos componentes gestionan internamente su propia interacción con el teclado y son una fuente frecuente de trampas de teclado. Confirme siempre que al pulsar Escape o una secuencia de teclas documentada se salga del editor y el foco vuelva al orden de tabulación normal de la página.
  • Suponer que, porque un componente usa elementos HTML nativos, no puede ser una trampa de teclado: un elemento <select> dentro de un formulario que usa JavaScript para sobrescribir su evento blur aún puede atrapar el foco. El uso de HTML semántico no garantiza un comportamiento libre de trampas de teclado cuando se superpone una gestión personalizada de eventos de JavaScript.
  • No proporcionar documentación en pantalla cuando se requiere una tecla no estándar para salir de un componente: WCAG 2.1.2 permite explícitamente componentes que requieren teclas no estándar para salir, siempre que se informe a la persona usuaria. Si su widget requiere pulsar F6 o una combinación de teclas personalizada para salir, debe comunicarlo claramente a la persona usuaria, idealmente mediante instrucciones visibles junto al componente o un anuncio de una región ARIA en vivo cuando el foco entra.

Relación con las Regulaciones de Accesibilidad de Turquía

La Circular Presidencial 2025/10 de Turquía, publicada en el Boletín Oficial n.º 32933 el 21 de junio de 2025, establece requisitos vinculantes de accesibilidad digital para una amplia gama de entidades del sector público y privado que operan en Turquía. La circular exige la conformidad con estándares de accesibilidad web reconocidos internacionalmente, alineándose con WCAG 2.1 Nivel AA como base, lo que abarca todos los criterios de Nivel A, incluido WCAG 2.1.2 Sin trampa de teclado.

WCAG 2.1.2 es un criterio de Nivel A, que representa el nivel mínimo de conformidad. En virtud de la circular, la conformidad de Nivel A es obligatoria para todas las entidades cubiertas. Las instituciones públicas deben lograr esta conformidad en el plazo de un año desde la publicación de la circular, mientras que a las entidades del sector privado se les conceden dos años para cumplirla.

Las entidades cubiertas por la circular son amplias e incluyen instituciones públicas y organismos gubernamentales en todos los niveles, plataformas de comercio electrónico y operadores de mercados en línea, bancos e instituciones de servicios financieros, hospitales y proveedores de atención sanitaria, empresas de telecomunicaciones con 200,000 o más abonados, agencias de viajes, empresas de transporte privado y escuelas privadas autorizadas por el Ministerio de Educación Nacional (MoNE). Esto significa que no abordar las trampas de teclado en un sitio web o aplicación web operado por cualquiera de estas entidades constituye una infracción directa de las regulaciones obligatorias de accesibilidad de Turquía.

Dada la prevalencia de fallos de trampa de teclado en aplicaciones web complejas — en particular en portales de banca en línea, sistemas de reserva de citas hospitalarias, flujos de pago de comercio electrónico y páginas de gestión de cuentas de telecomunicaciones — WCAG 2.1.2 merece una atención especial en el contexto de cumplimiento turco. Estos son exactamente los tipos de interfaces intensivas en interacción y basadas en JavaScript que tienen más probabilidades de contener widgets personalizados, cuadros de diálogo modales y elementos incrustados de terceros que pueden atrapar inadvertidamente a las personas usuarias de teclado.

Las organizaciones sujetas a la circular deben tratar las pruebas de trampas de teclado como una parte innegociable de su proceso de auditoría de accesibilidad. Dado que las herramientas automatizadas no pueden detectar de forma fiable las trampas de teclado, las entidades cubiertas deben invertir en pruebas manuales de teclado realizadas por especialistas en accesibilidad cualificados, idealmente incluyendo a personas con discapacidad, como parte de su camino hacia el cumplimiento. No remediar las trampas de teclado identificadas durante la auditoría representa no solo un riesgo legal en virtud de la circular, sino también una barrera significativa de acceso para las personas con discapacidades motoras y visuales que dependen de la navegación por teclado para utilizar los servicios digitales.