tabpanel
Marca el panel de contenido asociado a una pestaña. Cada tabpanel recibe su nombre de su pestaña mediante aria-labelledby; la pestaña referencia el panel mediante aria-controls. Se muestra un panel a la vez; los demás se ocultan con el atributo hidden.
Cuándo utilizarlo
En el contenedor que alberga el contenido de una pestaña. Cada tabpanel debe vincularse con su pestaña correspondiente mediante aria-labelledby (apuntando al id de la pestaña). La pestaña, a su vez, apunta al panel con aria-controls. Los paneles inactivos deben ocultarse con el atributo hidden — no únicamente con display: none vía CSS — para que el árbol de accesibilidad refleje el estado visual.
Si el panel contiene contenido interactivo (un formulario, una lista de enlaces), puede hacerse enfocable con tabindex="0". De este modo, tras activar una pestaña, el siguiente Tab aterriza dentro del panel.
Si el panel contiene una única región enfocable — por ejemplo, un encabezado principal seguido de contenido — basta con tabindex="0" en el panel. Si el panel está repleto de controles enfocables, el usuario llegará a ellos directamente con Tab y el tabindex es opcional.
Errores habituales
- Tabpanel oculto mediante CSS pero sin el atributo
hidden. Los lectores de pantalla pueden seguir exponiendo su contenido. - Tabpanel sin
aria-labelledby— se anuncia simplemente como «panel de pestañas» sin nombre. - La pestaña apunta al id del tabpanel mediante
aria-controls, pero el panel no apunta de vuelta mediantearia-labelledby. La asociación debe ser recíproca. - Todos los tabpanels presentes en el DOM con
display: nonegestionado dinámicamente, pero sin el atributohidden. Los auditores marcan esta incoherencia. - Uso de
role="tabpanel"en un panel que NO está asociado a ningúnrole="tab"— tabpanel huérfano.
Ejemplo
<div role="tablist" aria-label="Settings">
<button role="tab" id="t-account" aria-selected="true" aria-controls="p-account" tabindex="0">Account</button>
<button role="tab" id="t-billing" aria-selected="false" aria-controls="p-billing" tabindex="-1">Billing</button>
</div>
<section role="tabpanel" id="p-account" aria-labelledby="t-account" tabindex="0">
<h2>Account</h2>
<p>…</p>
</section>
<section role="tabpanel" id="p-billing" aria-labelledby="t-billing" tabindex="0" hidden>
<h2>Billing</h2>
<p>…</p>
</section>