Tabs
.tabs agrupa contenidos relacionados en paneles y muestra uno a la vez. Sigue el patrón tabs de ARIA. Necesita ds-uikit.js. Para enlazar a otras páginas usa Navegación.
div.tabscontiene la lista de pestañasdiv.tabs__list(role="tablist"yaria-label) y los paneles.- Cada pestaña es un
button.tabs__tabconrole="tab",id,aria-selected,aria-controls="<id del panel>"ydata-ds-toggle="tab". Las inactivas llevantabindex="-1". - Cada panel es un
div.tabs__panelconrole="tabpanel",aria-labelledby="<id de la pestaña>"ytabindex="0". Los inactivos llevanhidden.
El aspecto por defecto es una línea bajo la pestaña activa, como nav--underline.
<div class="tabs"> <div class="tabs__list" role="tablist" aria-label="Detalles del plan"> <button type="button" class="tabs__tab" role="tab" id="tab-resumen" aria-selected="true" aria-controls="panel-resumen" data-ds-toggle="tab">Resumen</button> <button type="button" class="tabs__tab" role="tab" id="tab-incluye" aria-selected="false" aria-controls="panel-incluye" tabindex="-1" data-ds-toggle="tab">Qué incluye</button> <button type="button" class="tabs__tab" role="tab" id="tab-plazos" aria-selected="false" aria-controls="panel-plazos" tabindex="-1" data-ds-toggle="tab">Plazos</button> </div> <div class="tabs__panel" role="tabpanel" id="panel-resumen" aria-labelledby="tab-resumen" tabindex="0"> <p>Un sitio web a la medida de tu negocio, listo para crecer contigo.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-incluye" aria-labelledby="tab-incluye" tabindex="0" hidden> <p>Diseño, desarrollo, hosting y soporte durante tres meses.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-plazos" aria-labelledby="tab-plazos" tabindex="0" hidden> <p>Entre tres y seis semanas, según la cantidad de páginas.</p> </div></div>export default function TabsBasico() { return ( <div className="tabs"> <div className="tabs__list" role="tablist" aria-label="Detalles del plan"> <button type="button" className="tabs__tab" role="tab" id="tab-resumen" aria-selected="true" aria-controls="panel-resumen" data-ds-toggle="tab">Resumen</button> <button type="button" className="tabs__tab" role="tab" id="tab-incluye" aria-selected="false" aria-controls="panel-incluye" tabIndex={-1} data-ds-toggle="tab">Qué incluye</button> <button type="button" className="tabs__tab" role="tab" id="tab-plazos" aria-selected="false" aria-controls="panel-plazos" tabIndex={-1} data-ds-toggle="tab">Plazos</button> </div> <div className="tabs__panel" role="tabpanel" id="panel-resumen" aria-labelledby="tab-resumen" tabIndex={0}> <p>Un sitio web a la medida de tu negocio, listo para crecer contigo.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-incluye" aria-labelledby="tab-incluye" tabIndex={0} hidden> <p>Diseño, desarrollo, hosting y soporte durante tres meses.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-plazos" aria-labelledby="tab-plazos" tabIndex={0} hidden> <p>Entre tres y seis semanas, según la cantidad de páginas.</p> </div> </div> );}Píldoras
Sección titulada «Píldoras»tabs--pills rellena la pestaña activa con primary, como nav--pills.
<div class="tabs tabs--pills"> <div class="tabs__list" role="tablist" aria-label="Facturación"> <button type="button" class="tabs__tab" role="tab" id="tab-mensual" aria-selected="true" aria-controls="panel-mensual" data-ds-toggle="tab">Mensual</button> <button type="button" class="tabs__tab" role="tab" id="tab-anual" aria-selected="false" aria-controls="panel-anual" tabindex="-1" data-ds-toggle="tab">Anual</button> </div> <div class="tabs__panel" role="tabpanel" id="panel-mensual" aria-labelledby="tab-mensual" tabindex="0"> <p>Pagas mes a mes y puedes cancelar cuando quieras.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-anual" aria-labelledby="tab-anual" tabindex="0" hidden> <p>Pagas una vez al año y te regalamos dos meses.</p> </div></div>export default function TabsPildoras() { return ( <div className="tabs tabs--pills"> <div className="tabs__list" role="tablist" aria-label="Facturación"> <button type="button" className="tabs__tab" role="tab" id="tab-mensual" aria-selected="true" aria-controls="panel-mensual" data-ds-toggle="tab">Mensual</button> <button type="button" className="tabs__tab" role="tab" id="tab-anual" aria-selected="false" aria-controls="panel-anual" tabIndex={-1} data-ds-toggle="tab">Anual</button> </div> <div className="tabs__panel" role="tabpanel" id="panel-mensual" aria-labelledby="tab-mensual" tabIndex={0}> <p>Pagas mes a mes y puedes cancelar cuando quieras.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-anual" aria-labelledby="tab-anual" tabIndex={0} hidden> <p>Pagas una vez al año y te regalamos dos meses.</p> </div> </div> );}Vertical
Sección titulada «Vertical»tabs--vertical pone las pestañas en columna, a la izquierda del panel. Agrega aria-orientation="vertical" a la lista: así los lectores de pantalla anuncian que se recorre con arriba y abajo. Se puede combinar con tabs--pills.
<div class="tabs tabs--vertical"> <div class="tabs__list" role="tablist" aria-label="Configuración" aria-orientation="vertical"> <button type="button" class="tabs__tab" role="tab" id="tab-perfil" aria-selected="true" aria-controls="panel-perfil" data-ds-toggle="tab">Perfil</button> <button type="button" class="tabs__tab" role="tab" id="tab-cuenta" aria-selected="false" aria-controls="panel-cuenta" tabindex="-1" data-ds-toggle="tab">Cuenta</button> <button type="button" class="tabs__tab" role="tab" id="tab-avisos" aria-selected="false" aria-controls="panel-avisos" tabindex="-1" data-ds-toggle="tab">Notificaciones</button> </div> <div class="tabs__panel" role="tabpanel" id="panel-perfil" aria-labelledby="tab-perfil" tabindex="0"> <p>Tu nombre, tu foto y cómo te ven los demás.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-cuenta" aria-labelledby="tab-cuenta" tabindex="0" hidden> <p>Correo, contraseña y métodos de pago.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-avisos" aria-labelledby="tab-avisos" tabindex="0" hidden> <p>Elige qué avisos recibes por correo.</p> </div></div>export default function TabsVertical() { return ( <div className="tabs tabs--vertical"> <div className="tabs__list" role="tablist" aria-label="Configuración" aria-orientation="vertical"> <button type="button" className="tabs__tab" role="tab" id="tab-perfil" aria-selected="true" aria-controls="panel-perfil" data-ds-toggle="tab">Perfil</button> <button type="button" className="tabs__tab" role="tab" id="tab-cuenta" aria-selected="false" aria-controls="panel-cuenta" tabIndex={-1} data-ds-toggle="tab">Cuenta</button> <button type="button" className="tabs__tab" role="tab" id="tab-avisos" aria-selected="false" aria-controls="panel-avisos" tabIndex={-1} data-ds-toggle="tab">Notificaciones</button> </div> <div className="tabs__panel" role="tabpanel" id="panel-perfil" aria-labelledby="tab-perfil" tabIndex={0}> <p>Tu nombre, tu foto y cómo te ven los demás.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-cuenta" aria-labelledby="tab-cuenta" tabIndex={0} hidden> <p>Correo, contraseña y métodos de pago.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-avisos" aria-labelledby="tab-avisos" tabIndex={0} hidden> <p>Elige qué avisos recibes por correo.</p> </div> </div> );}Pestaña deshabilitada
Sección titulada «Pestaña deshabilitada»Con disabled, la pestaña no se puede activar y el teclado la salta.
<div class="tabs"> <div class="tabs__list" role="tablist" aria-label="Pedido"> <button type="button" class="tabs__tab" role="tab" id="tab-productos" aria-selected="true" aria-controls="panel-productos" data-ds-toggle="tab">Productos</button> <button type="button" class="tabs__tab" role="tab" id="tab-envio" aria-selected="false" aria-controls="panel-envio" tabindex="-1" data-ds-toggle="tab">Envío</button> <button type="button" class="tabs__tab" role="tab" id="tab-pago" aria-selected="false" aria-controls="panel-pago" tabindex="-1" data-ds-toggle="tab" disabled>Pago</button> </div> <div class="tabs__panel" role="tabpanel" id="panel-productos" aria-labelledby="tab-productos" tabindex="0"> <p>Tienes tres productos en el carro.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-envio" aria-labelledby="tab-envio" tabindex="0" hidden> <p>Elige la dirección y el tipo de envío.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-pago" aria-labelledby="tab-pago" tabindex="0" hidden> <p>Paga con tarjeta o transferencia.</p> </div></div>export default function TabsDeshabilitada() { return ( <div className="tabs"> <div className="tabs__list" role="tablist" aria-label="Pedido"> <button type="button" className="tabs__tab" role="tab" id="tab-productos" aria-selected="true" aria-controls="panel-productos" data-ds-toggle="tab">Productos</button> <button type="button" className="tabs__tab" role="tab" id="tab-envio" aria-selected="false" aria-controls="panel-envio" tabIndex={-1} data-ds-toggle="tab">Envío</button> <button type="button" className="tabs__tab" role="tab" id="tab-pago" aria-selected="false" aria-controls="panel-pago" tabIndex={-1} data-ds-toggle="tab" disabled>Pago</button> </div> <div className="tabs__panel" role="tabpanel" id="panel-productos" aria-labelledby="tab-productos" tabIndex={0}> <p>Tienes tres productos en el carro.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-envio" aria-labelledby="tab-envio" tabIndex={0} hidden> <p>Elige la dirección y el tipo de envío.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-pago" aria-labelledby="tab-pago" tabIndex={0} hidden> <p>Paga con tarjeta o transferencia.</p> </div> </div> );}Con ícono
Sección titulada «Con ícono»Los íconos van dentro de la pestaña con aria-hidden="true". Si la pestaña solo tiene ícono, ponle aria-label.
<div class="tabs"> <div class="tabs__list" role="tablist" aria-label="Contacto"> <button type="button" class="tabs__tab" role="tab" id="tab-correo" aria-selected="true" aria-controls="panel-correo" data-ds-toggle="tab"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2" /><path d="m3 7 9 6 9-6" /></svg> Correo </button> <button type="button" class="tabs__tab" role="tab" id="tab-telefono" aria-selected="false" aria-controls="panel-telefono" tabindex="-1" data-ds-toggle="tab"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2" /></svg> Teléfono </button> </div> <div class="tabs__panel" role="tabpanel" id="panel-correo" aria-labelledby="tab-correo" tabindex="0"> <p>Escríbenos y te respondemos en menos de un día hábil.</p> </div> <div class="tabs__panel" role="tabpanel" id="panel-telefono" aria-labelledby="tab-telefono" tabindex="0" hidden> <p>Llámanos de lunes a viernes, de 9:00 a 18:00.</p> </div></div>export default function TabsIconos() { return ( <div className="tabs"> <div className="tabs__list" role="tablist" aria-label="Contacto"> <button type="button" className="tabs__tab" role="tab" id="tab-correo" aria-selected="true" aria-controls="panel-correo" data-ds-toggle="tab"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><rect x="3" y="5" width="18" height="14" rx="2" /><path d="m3 7 9 6 9-6" /></svg> Correo </button> <button type="button" className="tabs__tab" role="tab" id="tab-telefono" aria-selected="false" aria-controls="panel-telefono" tabIndex={-1} data-ds-toggle="tab"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d="M5 4h4l2 5-2.5 1.5a11 11 0 0 0 5 5L15 13l5 2v4a2 2 0 0 1-2 2A16 16 0 0 1 3 6a2 2 0 0 1 2-2" /></svg> Teléfono </button> </div> <div className="tabs__panel" role="tabpanel" id="panel-correo" aria-labelledby="tab-correo" tabIndex={0}> <p>Escríbenos y te respondemos en menos de un día hábil.</p> </div> <div className="tabs__panel" role="tabpanel" id="panel-telefono" aria-labelledby="tab-telefono" tabIndex={0} hidden> <p>Llámanos de lunes a viernes, de 9:00 a 18:00.</p> </div> </div> );}JavaScript
Sección titulada «JavaScript»- Clic: una pestaña con
data-ds-toggle="tab"muestra su panel (el dearia-controls). Si ya está activa, no pasa nada. - Teclado: las flechas izquierda y derecha (arriba y abajo en vertical) pasan a la pestaña anterior o siguiente y la activan; en los extremos dan la vuelta. Inicio y Fin van a la primera y a la última. Saltan las deshabilitadas.
- Actualiza
aria-selected,tabindex(solo la activa queda en el orden de Tab) yhiddende los paneles. - Transición: el panel anterior se desvanece (
is-closing) y después aparece el nuevo (is-opening). Los paneles se ocultan conhidden, sin quitarlos del DOM.
const plan = dsUIKit.tabs('#plan'); // .tabs o selectorplan.show(2); // índice (desde 0)plan.show(document.querySelector('#tab-plazos')); // o la pestañaEn React, los atributos funcionan igual. Para cambiar de pestaña desde código: tabs(ref.current).show(1) (con import { tabs } from './js/ds-uikit.esm.js').
| Evento | Cuándo |
|---|---|
ds:open |
En el panel nuevo, antes de mostrarse. Con preventDefault() se cancela el cambio. |
ds:opened |
En el panel nuevo, al terminar de mostrarse. |
ds:close |
En el panel anterior, antes de ocultarse. No se puede cancelar. |
ds:closed |
En el panel anterior, al terminar de ocultarse. |
event.detail.trigger es la pestaña que se activó, o null si fue por la API. Más detalles en JavaScript.
// Carga el contenido del panel la primera vez que se muestradocument.addEventListener('ds:open', (e) => { if (e.detail.component === 'tabs' && e.target.id === 'panel-plazos') cargarPlazos();});Accesibilidad
Sección titulada «Accesibilidad»- La lista lleva
role="tablist"y unaria-labelque describe el grupo. Cada pestaña apunta a su panel conaria-controlsy cada panel se nombra con su pestaña (aria-labelledby). - Teclado: Tab entra a la pestaña activa y sale al panel; las flechas recorren las pestañas (foco itinerante: solo la activa tiene
tabindex="0"). La pestaña se activa al recibir el foco. - El panel lleva
tabindex="0"para que se pueda llegar a él con Tab aunque no tenga elementos enfocables. Si su contenido empieza con un enlace o un botón, puedes quitarlo. - En vertical,
aria-orientation="vertical"en la lista. - Usa
disableden el<button>para deshabilitar una pestaña (también sirvearia-disabled="true"). - Los paneles inactivos están ocultos con
hidden: no se leen ni se enfocan. - Con «reducir movimiento» activado, cambia de panel sin transición.
| Clase | Uso |
|---|---|
tabs |
Contenedor de las pestañas y los paneles. |
tabs__list |
Lista de pestañas (role="tablist"). |
tabs__tab |
Pestaña (role="tab"). La activa lleva aria-selected="true". |
tabs__panel |
Panel (role="tabpanel"). Los inactivos llevan hidden. |
tabs--pills |
Píldoras: la pestaña activa se rellena con primary. |
tabs--vertical |
Pestañas en columna, a la izquierda del panel. |
is-opening, is-closing |
Estados del fundido (los pone el JS). |
Variables
Sección titulada «Variables»| Variable | Valor por defecto | Uso |
|---|---|---|
--ds-tabs-gap | var(--ds-space-4) | Separación entre las pestañas y el panel. |
--ds-tabs-list-gap | var(--ds-space-6) | Separación entre pestañas. |
--ds-tabs-list-border-width | 1px | Grosor de la línea bajo las pestañas. |
--ds-tabs-list-border-color | var(--ds-color-border) | Color de la línea bajo las pestañas. |
--ds-tabs-tab-padding-y | var(--ds-space-2) | Espacio vertical interno de la pestaña. |
--ds-tabs-tab-padding-x | 0 | Espacio horizontal interno de la pestaña. |
--ds-tabs-tab-gap | var(--ds-space-2) | Separación entre ícono y texto. |
--ds-tabs-tab-font-size | var(--ds-font-size-base) | |
--ds-tabs-tab-font-weight | var(--ds-font-weight-medium) | |
--ds-tabs-tab-color | var(--ds-color-text-muted) | Color de la pestaña. |
--ds-tabs-tab-bg | transparent | Fondo de la pestaña. |
--ds-tabs-tab-radius | 0 | Radio de las esquinas de la pestaña. |
--ds-tabs-tab-hover-color | var(--ds-color-text) | Color en hover. |
--ds-tabs-tab-hover-bg | var(--ds-tabs-tab-bg) | Fondo en hover. |
--ds-tabs-tab-active-color | var(--ds-color-primary) | Color de la pestaña activa. |
--ds-tabs-tab-active-bg | var(--ds-tabs-tab-bg) | Fondo de la pestaña activa. |
--ds-tabs-icon-size | 1.25em | Tamaño de los íconos. |
--ds-tabs-indicator-size | 3px | Grosor de la línea de la pestaña activa. |
--ds-tabs-indicator-color | currentColor | Color de la línea de la pestaña activa. |
--ds-tabs-focus-color | var(--ds-color-primary) | Color del contorno de foco. |
--ds-tabs-disabled-opacity | 0.5 | Opacidad de una pestaña deshabilitada. |
--ds-tabs-transition-duration | calc(var(--ds-duration-fast) * 0.75) | Duración de cada fundido al cambiar de panel (sale el anterior y entra el nuevo). |