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
SC | Nombre | Cómo afecta a tabs | Nivel |
---|---|---|---|
1.3.1, se abrirá en una nueva pestaña | Info and Relationships | Roles ARIA definen la relación pestaña-panel para usuarios de AT | A |
2.1.1, se abrirá en una nueva pestaña | Keyboard | Navegación y activación deben funcionar sin ratón | A |
2.4.3, se abrirá en una nueva pestaña | Focus Order | El foco pasa de la pestaña activa al panel en secuencia lógica | A |
2.4.7, se abrirá en una nueva pestaña | Focus Visible / 2.4.13, se abrirá en una nueva pestaña | El indicador de foco debe ser perceptible y cumplir las métricas de contraste/tamaño | AA |
2.4.11-12, se abrirá en una nueva pestaña | Focus Not Obscured | Nada debería tapar el foco al cambiar de pestaña | AA/AAA |
2.5.3, se abrirá en una nueva pestaña | Label in Name | El texto visible de la pestaña debe coincidir con su nombre accesible para que los comandos de voz funcionen | A |
3.2.1, se abrirá en una nueva pestaña | On Focus | Activar 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ña | Name, Role, Value | Los roles tab/tabpanel y los estados aria-selected deben ser actualizados en tiempo real | A |
1.4.11, se abrirá en una nueva pestaña | Non-text Contrast | Bordes, iconos o fondo de la pestaña activa necesitan 3:1 frente a elementos adyacentes | AA |
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 atributosaria-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>
};
Navegación for teclado Flechas derecha/izquierda y Home/End
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>
...
};