Las tabs (pestañas) son muy populares para agrupar contenido relacionado sin recargar la pantalla, pero también encierran algunos riesgos: parte de la información queda oculta, la interacción puede ser exclusivamente con el ratón y las tecnologías asistivas necesitan saber qué pestaña está activa.

Comportamiento de teclado y gestión del foco

Se definen los siguientes comportamientos de referencia (activación automática):

  • Tab entra en el tablist, aterriza en la pestaña activa y luego salta al tabpanel.
  • Flechas derecha/izquierda recorren las pestañas; al mover el foco se activa la nueva pestaña.
  • Home/End saltan a la primera o última pestaña.

Criterios de éxito (SC) asociados al componente de tabs

SCNombreCómo afecta a tabsNivel
1.3.1, se abrirá en una nueva pestañaInfo and RelationshipsRoles ARIA definen la relación pestaña-panel para usuarios de ATA
2.1.1, se abrirá en una nueva pestañaKeyboardNavegación y activación deben funcionar sin ratónA
2.4.3, se abrirá en una nueva pestañaFocus OrderEl foco pasa de la pestaña activa al panel en secuencia lógicaA
2.4.7, se abrirá en una nueva pestañaFocus Visible / 2.4.13, se abrirá en una nueva pestañaEl indicador de foco debe ser perceptible y cumplir las métricas de contraste/tamañoAA
2.4.11-12, se abrirá en una nueva pestañaFocus Not ObscuredNada debería tapar el foco al cambiar de pestañaAA/AAA
2.5.3, se abrirá en una nueva pestañaLabel in NameEl texto visible de la pestaña debe coincidir con su nombre accesible para que los comandos de voz funcionenA
3.2.1, se abrirá en una nueva pestañaOn FocusActivar la pestaña al recibir foco es válido siempre que no cambie de contexto (por ejemplo, no abra otra página)A
4.1.2, se abrirá en una nueva pestañaName, Role, ValueLos roles tab/tabpanel y los estados aria-selected deben ser actualizados en tiempo realA
1.4.11, se abrirá en una nueva pestañaNon-text ContrastBordes, iconos o fondo de la pestaña activa necesitan 3:1 frente a elementos adyacentesAA

Lista de verificación rápida

  • ¿Se puede navegar, seleccionar y leer cada panel sólo con teclado?
  • ¿Existe un foco visible y nunca queda fuera de pantalla?
  • ¿Roles tablist, tab, tabpanel y atributos aria-selected, aria-controls, aria-labelledby están correctos?
  • ¿El texto visible de la pestaña coincide con su nombre accesible?
  • ¿Bordes/íconos activos tienen contraste ≥ 3 : 1?

Ejemplo de implementación

Elemento seleccionado por defecto

useEffect(() => {
  tabsRef.current = tabsRef.current.slice(0, tabsData.length);
}, []);

Tab entra en el tablist, aterriza en la pestaña activa y luego salta al tabpanel.

export default function Tabs() {
  const [selectedIndex, setSelectedIndex] = useState(0);
  ...
  const onClick = idx => {
    setSelectedIndex(idx);
  };
  ...
  <button
    ...
    tabIndex={selectedIndex === idx ? 0 : -1}
    onClick={() => onClick(idx)}
  >...</button>
};
export default function Tabs() {
  const [selectedIndex, setSelectedIndex] = useState(0);
  ...
  const onKeyDown = (e, idx) => {
    let newIndex = idx;
    if (e.key === "ArrowRight") newIndex = (idx + 1) % tabsData.length;
    else if (e.key === "ArrowLeft") newIndex = (idx - 1 + tabsData.length) % tabsData.length;
    else if (e.key === "Home") newIndex = 0;
    else if (e.key === "End") newIndex = tabsData.length - 1;
    else return;

    e.preventDefault();
    setSelectedIndex(newIndex);
    tabsRef.current[newIndex].focus();
  };
  ...
  <button
    ...
    onKeyDown={e => onKeyDown(e, idx)}
  >...</button>
  ...
};

Referencias