Normativas · ARIA

Rol Estructura del documento

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 mediante aria-labelledby. La asociación debe ser recíproca.
  • Todos los tabpanels presentes en el DOM con display: none gestionado dinámicamente, pero sin el atributo hidden. Los auditores marcan esta incoherencia.
  • Uso de role="tabpanel" en un panel que NO está asociado a ningún role="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>