Ir al contenido

Componentes · Ola 2 · Navegación

Tabs

Pestañas para cambiar entre vistas del mismo contexto sin salir de la página. La pestaña activa se marca con la barra de luz de la selección; se recorren con las flechas y, si no entran, se desplazan de costado.

import { Tabs, TabsList, TabsTrigger, TabsPanel } from '@archeblack/ui';

Ejemplos

Pestañas

Tabs con bind:value, un TabsList con nombre, un TabsTrigger por pestaña y un TabsPanel por panel, unidos por value.

Tráfico estable. 184.302 solicitudes en las últimas 24 horas.

Svelte
<script lang="ts">
	import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@archeblack/ui';

	let value = $state('summary');
</script>

<div style="width: 100%">
	<Tabs bind:value>
		<TabsList aria-label="Secciones del proyecto">
			<TabsTrigger value="summary">Resumen</TabsTrigger>
			<TabsTrigger value="deploys">Despliegues</TabsTrigger>
			<TabsTrigger value="logs">Registros</TabsTrigger>
			<TabsTrigger value="settings">Ajustes</TabsTrigger>
		</TabsList>
		<TabsPanel value="summary">
			<p style="margin: 0">Tráfico estable. 184.302 solicitudes en las últimas 24 horas.</p>
		</TabsPanel>
		<TabsPanel value="deploys">
			<p style="margin: 0">Último despliegue hace 6 minutos desde la rama main.</p>
		</TabsPanel>
		<TabsPanel value="logs">
			<p style="margin: 0">Sin errores en la última hora.</p>
		</TabsPanel>
		<TabsPanel value="settings">
			<p style="margin: 0">Dominio, variables de entorno y permisos del equipo.</p>
		</TabsPanel>
	</Tabs>
</div>

Activación manual

Con activationMode='manual', las flechas mueven el foco y Enter o espacio abren la pestaña.

1.204 visitas únicas.

Svelte
<script lang="ts">
	import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@archeblack/ui';

	let value = $state('day');
</script>

<!--
	Activación manual: las flechas solo mueven el foco y la pestaña se abre con Enter o espacio.
	Sirve cuando abrir un panel es costoso (una consulta, un gráfico pesado).
-->
<div style="width: 100%">
	<Tabs bind:value activationMode="manual">
		<TabsList aria-label="Período del informe">
			<TabsTrigger value="day">Hoy</TabsTrigger>
			<TabsTrigger value="week">Últimos 7 días</TabsTrigger>
			<TabsTrigger value="month">Últimos 30 días</TabsTrigger>
		</TabsList>
		<TabsPanel value="day"><p style="margin: 0">1.204 visitas únicas.</p></TabsPanel>
		<TabsPanel value="week"><p style="margin: 0">8.911 visitas únicas.</p></TabsPanel>
		<TabsPanel value="month"><p style="margin: 0">36.502 visitas únicas.</p></TabsPanel>
	</Tabs>
</div>

Con conteo y deshabilitada

Una pestaña admite un Badge sm con un conteo. disabled apaga una pestaña y las flechas la saltan.

12 incidencias esperan respuesta.

Svelte
<script lang="ts">
	import { Badge, Tabs, TabsList, TabsPanel, TabsTrigger } from '@archeblack/ui';

	let value = $state('open');
</script>

<div style="width: 100%">
	<Tabs bind:value>
		<TabsList aria-label="Incidencias">
			<TabsTrigger value="open">Abiertas <Badge size="sm">12</Badge></TabsTrigger>
			<TabsTrigger value="closed">Cerradas <Badge size="sm">148</Badge></TabsTrigger>
			<TabsTrigger value="archived" disabled>Archivadas</TabsTrigger>
		</TabsList>
		<TabsPanel value="open"><p style="margin: 0">12 incidencias esperan respuesta.</p></TabsPanel>
		<TabsPanel value="closed"><p style="margin: 0">148 incidencias cerradas este año.</p></TabsPanel
		>
		<TabsPanel value="archived"><p style="margin: 0">Sin incidencias archivadas.</p></TabsPanel>
	</Tabs>
</div>

Muchas pestañas

Si no entran, la fila se desplaza de costado y el borde que esconde pestañas se desvanece. Con las flechas, la pestaña enfocada se trae a la vista entera.

Ajustes de «General».

Svelte
<script lang="ts">
	import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@archeblack/ui';

	const sections = [
		['general', 'General'],
		['domains', 'Dominios'],
		['env', 'Variables de entorno'],
		['team', 'Equipo'],
		['billing', 'Facturación'],
		['integrations', 'Integraciones'],
		['security', 'Seguridad'],
		['advanced', 'Avanzado']
	];

	let value = $state('general');
	const label = $derived(sections.find(([key]) => key === value)?.[1]);
</script>

<!-- El contenedor angosto simula una pantalla chica: la fila se desplaza de costado. -->
<div style="width: 100%; max-width: 22rem">
	<Tabs bind:value>
		<TabsList aria-label="Ajustes del proyecto">
			{#each sections as [key, name] (key)}
				<TabsTrigger value={key}>{name}</TabsTrigger>
			{/each}
		</TabsList>
		{#each sections as [key] (key)}
			<TabsPanel value={key}><p style="margin: 0">Ajustes de «{label}».</p></TabsPanel>
		{/each}
	</Tabs>
</div>

Vertical

orientation='vertical' pone la lista a un costado; se recorre con las flechas arriba y abajo. En un idioma de derecha a izquierda, la lista y su barra pasan al otro lado.

Nombre, foto y zona horaria.

Svelte
<script lang="ts">
	import { Tabs, TabsList, TabsPanel, TabsTrigger } from '@archeblack/ui';

	let value = $state('profile');
</script>

<!-- En vertical se recorre con las flechas arriba y abajo. En un móvil, mejor en horizontal. -->
<div style="width: 100%">
	<Tabs bind:value orientation="vertical">
		<TabsList aria-label="Cuenta">
			<TabsTrigger value="profile">Perfil</TabsTrigger>
			<TabsTrigger value="notifications">Notificaciones</TabsTrigger>
			<TabsTrigger value="sessions">Sesiones</TabsTrigger>
		</TabsList>
		<TabsPanel value="profile">
			<p style="margin: 0">Nombre, foto y zona horaria.</p>
		</TabsPanel>
		<TabsPanel value="notifications">
			<p style="margin: 0">Qué avisos llegan por correo y cuáles solo en la consola.</p>
		</TabsPanel>
		<TabsPanel value="sessions">
			<p style="margin: 0">Dos sesiones abiertas: este navegador y la app del teléfono.</p>
		</TabsPanel>
	</Tabs>
</div>

Props

Tabs

Props de Tabs
PropDescripción
bind:value string El value de la pestaña activa. Con bind:value el producto sabe cuál está abierta.
onValueChange? (value: string) => void Se llama al cambiar de pestaña, con su valor, después de actualizar value y solo si el cambio se aceptó (si un bind:value de función lo rechaza, no se llama). No es onchange, para no tapar el evento change que sube desde los formularios de los paneles.
activationMode? 'automatic' | 'manual' Por defecto 'automatic'automatic: la pestaña se abre al llegar con las flechas. manual: las flechas mueven el foco y Enter o espacio la abren.
orientation? 'horizontal' | 'vertical' Por defecto 'horizontal'vertical pone la lista a un costado del panel y se recorre con las flechas arriba y abajo.
loop? boolean Por defecto trueSi las flechas vuelven a la primera pestaña después de la última.
disabled? boolean Por defecto falseDeshabilita todas las pestañas.
children Snippet Un TabsList y un TabsPanel por pestaña.
class? ClassValue Clases del producto; se suman a arche-tabs.
...rest HTMLAttributes<HTMLDivElement> Cualquier otro atributo va al <div> raíz.

TabsList

Props de TabsList
PropDescripción
aria-label | aria-labelledby string Nombre de la lista: un aria-label («Secciones del proyecto») o el id del título visible que la nombra. Uno de los dos es obligatorio por tipos.
children Snippet Las pestañas (TabsTrigger).
class? ClassValue Clases del producto; se suman a arche-tabs__list.
...rest HTMLAttributes<HTMLDivElement> Van al <div role="tablist">.

TabsTrigger

Props de TabsTrigger
PropDescripción
value string Identifica la pestaña. Su TabsPanel lleva el mismo.
disabled? boolean Por defecto falseDeshabilita esta pestaña: las flechas la saltan.
children Snippet El texto de la pestaña; admite un Badge con un conteo.
class? ClassValue Clases del producto; se suman a arche-tabs__trigger.
...rest HTMLButtonAttributes Van al <button role="tab">.

TabsPanel

Props de TabsPanel
PropDescripción
value string El value de la pestaña que muestra este panel.
tabindex? 0 | -1 Por defecto 0El panel es enfocable para llegar al contenido con Tab. Si empieza con un control enfocable, puede ir -1.
children Snippet El contenido del panel.
class? ClassValue Clases del producto; se suman a arche-tabs__panel.
...rest HTMLAttributes<HTMLDivElement> Van al <div role="tabpanel">.

Accesibilidad

  • Sigue el patrón de pestañas de ARIA con Bits UI: role="tablist", tab y tabpanel, con aria-selected, aria-controls y aria-labelledby unidos por id.
  • Con Tab se llega a la pestaña activa (las demás tienen tabindex="-1") y el siguiente Tab pasa al panel. Dentro de la lista se usan las flechas (izquierda y derecha, o arriba y abajo en vertical), Home y End.
  • Por defecto, la pestaña se abre al llegar con las flechas. Si abrir un panel es lento, usa activationMode="manual": las flechas solo mueven el foco y Enter o espacio abren.
  • La lista necesita un nombre: aria-label o aria-labelledby. TypeScript lo exige.
  • La pestaña activa se marca con aria-selected, el texto en text-strong y la barra de 2 px de selected, no solo con el color. En colores forzados la barra pasa a Highlight.
  • Si no entran, las pestañas se desplazan de costado sin barra de scroll. El borde que esconde pestañas se desvanece: es la señal de que la fila sigue. La pestaña que recibe el foco (con las flechas o con Tab) o que queda activa se trae a la vista entera, fuera del borde desvanecido y con su anillo de foco; solo se mueve la fila, no la página.
  • Una pestaña deshabilitada se anuncia como no disponible y las flechas la saltan. Si nunca se podrá usar, mejor no mostrarla.

Qué evitar

  • Pestañas para pasos de un proceso en orden (datos, pago, confirmación). Un formulario por pasos con botones de «Siguiente» y «Atrás».
  • Pestañas que llevan a otras páginas (cambian la URL). Una navegación con links y aria-current="page"; las pestañas cambian vistas dentro de la misma página.
  • Contenido que la persona necesita comparar, repartido en dos pestañas. Mostrarlo junto, en columnas o en una tabla.
  • Una sola pestaña, o más de siete. Sin pestañas si hay una sola vista; un menú o una lista lateral si hay muchas.
  • Íconos solos como pestañas. Texto corto en cada pestaña; un ícono solo no dice lo mismo a todos.