Recorrido del orden de tabulación

Pulsa Tab en una página web y el foco salta al siguiente enlace, botón o campo. Esta comprobación escribe la ruta completa para cualquier dirección y señala los controles que se quedaron fuera de ella.

Una pasada por la página y como máximo cinco de sus propias hojas de estilo. No se guarda nada.

O prueba mozilla.org, wikipedia.org

El foco es el cursor del teclado

Un ratón puede apuntar a cualquier sitio. Un teclado solo puede estar “sobre” un elemento a la vez, y esa posición se llama foco. Tab lo avanza, Shift+Tab lo retrocede, Enter sigue un enlace y Espacio pulsa un botón. Así se mueven las personas con temblores o con movilidad reducida en las manos. También los usuarios de lectores de pantalla, de dispositivos de conmutador o de control por voz, y cualquiera que rellene formularios deprisa.

El navegador deduce qué puede recibir el foco solo a partir del HTML. Los enlaces con href, los botones, los campos de formulario, <summary>, los reproductores multimedia con controles y los marcos están en la ruta por defecto, en el orden en que aparecen en el código fuente. Todo lo demás se omite, a menos que un atributo tabindex diga otra cosa.

Los tres significados de tabindex

ValorEfectoCuándo usarlo
tabindex="0"Añade el elemento a la ruta en su posición naturalControles personalizados que no pueden ser un <button> nativo
tabindex="-1"Lo quita de la ruta; los scripts aún pueden darle el focoPestañas inactivas, elementos de menú a los que se llega con las flechas, diapositivas de carrusel fuera de pantalla
tabindex="1" o másLo coloca por delante de todo lo demás, primero el número más bajoCasi nunca. El foco salta por la pantalla

El recorrido sigue estas reglas al pie de la letra: primero los valores positivos en orden ascendente y luego el orden del código fuente. Un grupo de botones de opción con el mismo name cuenta como una sola parada, ya que las flechas se mueven dentro del grupo. Los controles deshabilitados, los elementos <dialog> cerrados, las zonas inert o hidden y el contenido de los <details> cerrados se dejan fuera.

Qué señala la comprobación

Elementos clicables a los que el teclado no llega
Un <div onclick> o un elemento con role="button" y sin tabindex. Los usuarios de ratón pueden pulsarlo, y los de teclado ni siquiera pueden llegar a él. Sustitúyelo por <button type="button">, que trae de serie el foco, Enter y Espacio.
Enlaces sin href
Para el navegador, un <a> sin destino es texto normal, y Tab pasa de largo.
Controles con tabindex="-1"
Se informa de ellos, salvo que el enlace duplique otro enlace alcanzable a la misma dirección, o pertenezca a una lista de pestañas, un menú, una barra de herramientas o un grupo similar donde mandan las flechas.
Enlace para saltar al contenido
La comprobación busca en las tres primeras paradas un enlace a un ancla de la misma página, como <a href="#content">Skip to content</a>, y confirma que existe el id de destino. También cuenta cuántas pulsaciones hacen falta para ir del inicio de la página a <main>. Con cinco o menos, la falta de ese enlace solo se anota a título informativo.
Contorno de foco
Buscamos en los bloques <style> de la página, en los estilos en línea y en hasta cinco hojas de estilo del mismo sitio reglas como a:focus { outline: none } o * { outline: 0 }. Si nada sustituye el contorno con estilos de :focus-visible o de :focus, el hallazgo es rojo, porque los usuarios de teclado no pueden ver dónde están.
Otras paradas que merece la pena revisar
Elementos enfocables dentro de aria-hidden="true", paradas sin nombre accesible, marcos sin title y enlaces a # o a javascript: que hacen de botones.

Recuperar un foco visible

Los diseñadores suelen quitar el contorno porque aparece tras un clic de ratón y les parece feo. El selector :focus-visible zanja la discusión. Solo coincide cuando el navegador juzga que el usuario necesita ver el foco, lo que en la práctica significa uso del teclado.

:focus-visible {
  outline: 3px solid #1a5fb4;
  outline-offset: 2px;
}

WCAG 2.2 pide un indicador con un contraste de al menos 3:1 respecto a los colores que lo rodean. El patrón :focus:not(:focus-visible) { outline: none } es seguro y no se señala.

Límites de una lectura estática

La ruta se calcula a partir del HTML que recibe nuestro servidor. Los manejadores de clic añadidos en JavaScript con addEventListener no dejan rastro en el código, así que un <div> conectado de ese modo pasa inadvertido. Los menús construidos por script también son invisibles aquí, igual que las trampas de teclado, en las que el foco entra en un widget y no puede salir. Las hojas de estilo de otro dominio, o más allá de la quinta, no se leen.

Termina con la prueba real, que lleva dos minutos. Aparta el ratón, carga la página y pulsa Tab hasta llegar al pie de página. Siempre deberías ver dónde estás, y deberías poder abrir todos los menús y enviar todos los formularios.

Preguntas frecuentes

¿Cómo pruebo yo mismo la navegación con teclado?

Haz clic en la barra de direcciones y pulsa Tab una y otra vez. Shift+Tab retrocede, Enter funciona en los enlaces, Espacio en los botones y las casillas de verificación, las flechas dentro de los grupos de opciones y los menús, y Escape cierra los diálogos. En Safari de macOS, activa antes “Press Tab to highlight each item on a webpage” en los ajustes avanzados.

¿Basta con tabindex="0" en un div para que sea un botón?

No. Hace que el elemento pueda recibir el foco, pero Enter y Espacio no hacen nada hasta que añades manejadores de teclado, y un lector de pantalla no lo llamará botón sin role="button". Un <button> nativo te da las tres cosas.

¿Necesito un enlace para saltar al contenido si mi página tiene un elemento main?

Sí, para los usuarios de teclado que ven. Los lectores de pantalla pueden saltar a la región <main> con un atajo. Quien usa solo la tecla Tab no tiene ese atajo y tiene que pasar por cada elemento del menú, a menos que ofrezcas un enlace para saltar.

¿Por qué el orden de Tab no coincide con lo que veo en pantalla?

Tab sigue el orden del código fuente HTML, no el diseño visual. CSS como order, flex-direction: row-reverse, la colocación en cuadrícula o el posicionamiento absoluto puede mover elementos en pantalla sin cambiar su lugar en el código. Un tabindex positivo tiene el mismo efecto.

¿Es outline: none siempre un fallo de accesibilidad?

Solo cuando nada lo sustituye. Una regla que quita el contorno y define un borde, un fondo o una sombra visibles al recibir el foco en el mismo bloque está bien, y esta comprobación la cuenta como sustituto.