Navegación

Estado: Beta

Pestañas animadas

Para secciones hermanas de una vista (horario, precios, maestras): la pastilla de fondo mide la pestaña elegida y viaja hasta ella con resorte, y el panel cambia con un fundido corto mientras la caja ajusta su altura. Patrón ARIA tabs completo, con flechas, Inicio y Fin.

  • Lun 7:00 Vinyasa Ana
  • Mié 19:00 Reformer Sofía
  • Sáb 9:00 Barre Lucía

app/views/… (la demo de arriba)

<div class="w-full max-w-sm">
  <%= render Ui::AnimatedTabsComponent.new(label: "Información del estudio", id: "animated-tabs-demo") do |tabs| %>
    <% tabs.with_tab(label: "Horario", id: "horario") do %>
      <ul class="divide-y divide-border-subtle rounded-xl border border-border bg-card">
        <% [ [ "Lun 7:00", "Vinyasa", "Ana" ], [ "Mié 19:00", "Reformer", "Sofía" ], [ "Sáb 9:00", "Barre", "Lucía" ] ].each do |time, name, teacher| %>
          <li class="flex items-center gap-3 px-3.5 py-2.5">
            <span class="w-16 shrink-0 font-mono text-xs text-faint"><%= time %></span>
            <span class="min-w-0 flex-1 truncate text-foreground"><%= name %></span>
            <span class="shrink-0 text-xs"><%= teacher %></span>
          </li>
        <% end %>
      </ul>
    <% end %>
    <% tabs.with_tab(label: "Precios", id: "precios") do %>
      <ul class="divide-y divide-border-subtle rounded-xl border border-border bg-card">
        <% [ [ "Clase suelta", "$180" ], [ "Paquete de 10", "$1,450" ] ].each do |name, price| %>
          <li class="flex items-center justify-between gap-3 px-3.5 py-2.5">
            <span class="text-foreground"><%= name %></span>
            <span class="font-mono text-xs text-foreground"><%= price %></span>
          </li>
        <% end %>
      </ul>
      <p class="mt-2.5 px-1 text-xs">Los créditos de paquete duran 30 días.</p>
    <% end %>
    <% tabs.with_tab(label: "Maestras", id: "maestras") do %>
      <ul class="space-y-2.5 px-1">
        <% [ [ "AR", "Ana Ruiz", "Vinyasa y meditación" ], [ "SM", "Sofía Méndez", "Reformer" ], [ "LC", "Lucía Cano", "Barre" ], [ "PV", "Paula Vega", "Yin y restaurativa" ] ].each do |initials, name, focus| %>
          <li class="flex items-center gap-3">
            <span class="grid size-8 shrink-0 place-items-center rounded-full border border-border bg-raised font-mono text-2xs text-muted-foreground" aria-hidden="true"><%= initials %></span>
            <span class="min-w-0 leading-tight">
              <span class="block truncate text-foreground"><%= name %></span>
              <span class="block truncate text-xs"><%= focus %></span>
            </span>
          </li>
        <% end %>
      </ul>
    <% end %>
  <% end %>
</div>

Qué hace

  • La pastilla viaja con el resorte "morph" en x y ancho, medidos sobre la pestaña real.
  • El panel que sale se desvanece encima del que entra; la altura cambia con "gentle".
  • Dispara animated-tabs:change con { id, index } para cargar contenido o guardar la pestaña.
  • Si cambian las fuentes o el ancho, la pastilla se reacomoda sin animar.

Props

Nombre Tipo Default Descripción
label String — Nombre accesible de la lista de pestañas (aria-label).
selected String nil id de la pestaña activa al cargar; si no, la primera.
shape :segment | :pill :segment :pill redondea lista y pastilla por completo (variant: es su nombre anterior).
id String aleatorio Prefijo de los ids de pestañas y paneles.
with_tab(label:, id:, badge: nil) { } Slot — Una pestaña; el bloque es el contenido de su panel.

Accesibilidad