Pestañas accesibles con HTML, CSS y JavaScript

Crea pestañas accesibles con HTML, CSS y JavaScript. Incluye selección, paneles asociados y navegación con flechas, Inicio y Fin.

Cuándo convienen unas pestañas

Las pestañas accesibles HTML presentan alternativas del mismo contexto sin convertirlas en páginas independientes. Descripción y detalles de un producto pueden encajar. Los pasos obligatorios de un proceso no deberían disfrazarse de pestañas si necesitan completarse en orden. Empieza por la relación del contenido y después decide el patrón visual.

Asociar controles y paneles

Cada pestaña tiene un identificador y apunta a su panel. El panel utiliza el identificador de la pestaña como nombre. Copiar el bloque exige cambiar esas referencias de forma coherente. Un error de una letra puede ocultar el panel equivocado o dejar la interfaz sin contenido. Los identificadores también deben ser únicos dentro del documento, aunque dos grupos compartan las mismas etiquetas visibles.

Código completo para probar en tu navegador

Copia este documento en un archivo index.html, guarda los cambios y ábrelo en el navegador. El ejemplo no requiere bibliotecas.

<!doctype html>
<html lang="es">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pestañas accesibles con HTML, CSS y JavaScript</title>
<style>
*{box-sizing:border-box}body{font:16px/1.6 system-ui,sans-serif;max-width:900px;margin:2rem auto;padding:1rem;color:#17233b}label{display:block;font-weight:700;margin-top:1rem}input,textarea,select,button{font:inherit;padding:.65rem;max-width:100%}textarea{width:100%}button{cursor:pointer}button:focus-visible,a:focus-visible{outline:3px solid #173b94;outline-offset:3px}[hidden]{display:none!important}[role=tab][aria-selected=true]{background:#173b94;color:white}[role=tab]{border:1px solid #bdcbe2;border-radius:.5rem}[role=tabpanel]{padding:1rem;border:1px solid #bdcbe2}
</style>
</head>
<body>
<div id="pestanas">
 <div role="tablist" aria-label="Información del producto">
  <button type="button" role="tab" id="tab-a" aria-controls="panel-a" aria-selected="true">Descripción</button>
  <button type="button" role="tab" id="tab-b" aria-controls="panel-b" aria-selected="false" tabindex="-1">Detalles</button>
 </div>
 <section id="panel-a" role="tabpanel" aria-labelledby="tab-a" tabindex="0">Descripción del producto.</section>
 <section id="panel-b" role="tabpanel" aria-labelledby="tab-b" tabindex="0" hidden>Detalles del producto.</section>
</div>
<script>
const raiz = document.querySelector('#pestanas');
const tabs = [...raiz.querySelectorAll('[role=tab]')];
function activar(tab, foco = false) {
 tabs.forEach(t => {
  const activo = t === tab;
  t.setAttribute('aria-selected', String(activo));
  t.tabIndex = activo ? 0 : -1;
  document.getElementById(t.getAttribute('aria-controls')).hidden = !activo;
 });
 if (foco) tab.focus();
}
tabs.forEach((tab, i) => {
 tab.addEventListener('click', () => activar(tab));
 tab.addEventListener('keydown', e => {
  let n;
  if (e.key === 'ArrowRight') n = (i + 1) % tabs.length;
  if (e.key === 'ArrowLeft') n = (i - 1 + tabs.length) % tabs.length;
  if (e.key === 'Home') n = 0;
  if (e.key === 'End') n = tabs.length - 1;
  if (n !== undefined) { e.preventDefault(); activar(tabs[n], true); }
 });
});
</script>
</body>
</html>

Selección y foco no son lo mismo

Aria-selected representa la selección. Tabindex determina qué control participa en el recorrido secuencial. El ejemplo mantiene una sola pestaña en ese recorrido y ofrece flechas para moverse entre ellas. No marques todas como seleccionadas ni mantengas el panel anterior visible por error. Al pulsar Tab, la persona debe poder abandonar el grupo y continuar por el contenido pertinente.

Activación automática o manual

Este ejemplo activa el panel al desplazarse con las flechas, adecuado para contenido local que aparece sin espera. Si cada cambio requiere una operación lenta, considera un patrón de activación manual con Intro o Espacio. La decisión afecta la comodidad del teclado. No hagas que explorar las etiquetas lance una serie de peticiones que bloquean el componente.

Ocultar sin dejar interacciones invisibles

Hidden retira de la presentación el panel inactivo según su comportamiento nativo. No utilices únicamente opacidad cero mientras los enlaces del panel permanecen en el recorrido. La persona podría enfocar controles que no ve. Si cambias la forma de ocultación mediante CSS, prueba de nuevo teclado y tecnologías de asistencia: una transición decorativa no debe romper el estado del componente.

Estilos que explican el estado

Distingue la pestaña seleccionada con una señal que no dependa solo del color, como un borde o subrayado. Conserva un indicador de foco diferente para saber dónde actuará el teclado. Permite que las etiquetas crezcan y se adapten en móvil. No metas una navegación extensa dentro de una fila que necesita precisión horizontal para encontrar la última opción.

Pruebas del recorrido

Activa cada pestaña con ratón y teclado. Comprueba derecha, izquierda, Inicio y Fin, incluidos los extremos. Recorre después el panel activo y asegúrate de no entrar en uno oculto. Duplica el componente con referencias nuevas y confirma que los grupos no se afectan entre sí. Si añades una tercera pestaña, actualiza el HTML y verifica que el array la incluye sin otra lógica especial.

Contenido recuperable

Si la información es importante para todas las personas, valora mostrarla directamente con encabezados. Una pestaña no debe utilizarse para esconder condiciones esenciales ni compensar una página sin jerarquía. Antes de publicar, comprueba además cómo se presenta sin JavaScript y define una mejora progresiva apropiada para tu proyecto. El código del ejemplo muestra el mecanismo de interacción; la estrategia de respaldo debe responder al uso real.

Añadir una tercera pestaña sin romper relaciones

Para ampliar el ejemplo necesitas un botón con identificador nuevo y un panel cuyo id coincida con aria-controls. El panel utiliza aria-labelledby para recuperar el nombre del botón. No dupliques los identificadores del primer par al copiar y pegar. La colección de pestañas se construye al inicializar el script, así que una tercera pestaña escrita en el HTML se incorpora al recorrido. Si la agregas después mediante JavaScript, tendrás que actualizar la colección y registrar su interacción. Comprueba las relaciones en ambos sentidos: un nombre visible correcto no compensa que el control esté asociado a otro panel.

Cuándo usar navegación normal

Si cada opción abre una página diferente, probablemente necesitas enlaces dentro de una navegación, no el patrón de pestañas que oculta paneles locales. Los enlaces ofrecen direcciones, historial y opciones como abrir otra pestaña del navegador. No les asignes role tab solamente para conseguir una apariencia visual. Del mismo modo, si la información debe leerse en secuencia, varios apartados con encabezados pueden ser más apropiados que ocultar contenido. Elegir un componente por el comportamiento que necesita el usuario evita tener que reconstruir funciones nativas que los enlaces ya resuelven.

Una estrategia de mejora progresiva

El ejemplo ya contiene hidden en el segundo panel y depende del script para cambiar de selección. En un producto donde el contenido deba permanecer disponible si JavaScript falla, empieza mostrando los apartados y aplica la estructura interactiva únicamente después de inicializarla con éxito. Eso requiere coordinar roles, estados y presentación, no simplemente borrar un atributo suelto. Otra opción es ofrecer enlaces a una versión completa. Define la importancia del contenido antes de elegir la estrategia. Para una descripción contractual o instrucciones esenciales, un fallo de interacción no debería impedir acceder a la información.

Tabulación dentro de paneles con controles

El tabindex del panel ayuda cuando su contenido inicial no contiene ningún elemento interactivo. Si el panel empieza con un enlace o un campo claramente identificado, puede ser conveniente revisar esa parada adicional según el patrón aplicado. El usuario entra en la lista por la pestaña seleccionada y utiliza flechas para recorrer sus opciones. Al pulsar Tab continúa al contenido activo. Los controles del panel oculto no deben recibir foco. Prueba también Shift y Tab para volver atrás; los recorridos inversos suelen descubrir estados que no aparecen cuando solamente se hacen clics con el ratón.

Validar estados después de una interacción rápida

Pulsa varias flechas seguidas y alterna clics con teclado. Debe existir una sola pestaña seleccionada y un solo panel visible. Comprueba que el foco visible coincide con el control que recibe las teclas. Si añades carga remota, no mantengas activación automática cuando cada cambio obliga a esperar: puede ser más apropiado un patrón de activación manual. Documenta esa decisión y prueba sus estados de espera. Una apariencia convincente no basta si la selección, el contenido y el recorrido accesible terminan indicando cosas distintas después de una interacción rápida.

Guías relacionadas para seguir practicando

Puedes ampliar esta práctica con Usar HTML semántico y Escuchar eventos con JavaScript.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

logo mastercode pro
Resumen de privacidad

Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.

Puedes ver mas detalle en nuestra pagina de privcidad