Tooltip direccional

Para nombrar los botones de ícono del admin, explicar una acción o mostrar el dato que no cupo. Nace pegado a su disparador y se aleja con resorte hacia su lado; la primera vez espera un momento y, al recorrer una barra de botones, el siguiente sale de inmediato. Flota en la capa superior y se voltea si no cabe.

Exportar CSV⌘E Duplicar semana Pasar lista de Vinyasa Flow · 7:00C

Regina Castro Fernández

regina.castro.fernandez@lunaestudio.mx

regina.castro.fernandez@lunaestudio.mx
4/10

app/views/… (la demo de arriba)

<% icon_button = "grid size-9 place-items-center rounded-lg text-muted-foreground transition-colors duration-quick hover:bg-surface-hover hover:text-foreground eui-press eui-focus" %>
<div class="flex w-full max-w-xs flex-col items-center gap-7">
  <%# Botones de ícono del horario del admin: el aria-label nombra el botón y
      el tooltip lo muestra (no se lee dos veces). %>
  <div class="flex items-center gap-0.5 rounded-xl border border-border bg-card p-1 shadow-subtle" role="group" aria-label="Acciones del horario">
    <%= render Ui::TooltipComponent.new(text: "Exportar CSV", shortcut: [ "⌘", "E" ]) do %>
      <button type="button" class="<%= icon_button %>" aria-label="Exportar CSV">
        <%= render Ui::IconComponent.new(:download) %>
      </button>
    <% end %>

    <%# Enganchado por selector: el botón no cambia, el tooltip lo busca. %>
    <button type="button" class="<%= icon_button %>" aria-label="Duplicar semana" data-tooltip-demo="duplicar">
      <%= render Ui::IconComponent.new(:copy) %>
    </button>
    <%= render Ui::TooltipComponent.new(text: "Duplicar semana", side: :bottom, selector: "[data-tooltip-demo=duplicar]") %>

    <span class="mx-1 h-5 w-px bg-border" aria-hidden="true"></span>

    <%= render Ui::TooltipComponent.new(text: "Pasar lista de Vinyasa Flow · 7:00", shortcut: "C") do %>
      <button type="button" class="<%= icon_button %>" aria-label="Check-in">
        <%= render Ui::IconComponent.new(:user_check) %>
      </button>
    <% end %>
  </div>

  <%# Un dato que no cabe: el tooltip solo aparece si de verdad está recortado. %>
  <div class="flex w-full items-center gap-3 rounded-xl border border-border bg-card px-3.5 py-3">
    <span class="grid size-9 shrink-0 place-items-center rounded-full border border-border-subtle bg-raised text-2xs font-medium text-muted-foreground" aria-hidden="true">RC</span>
    <div class="min-w-0 flex-1">
      <p class="truncate text-sm font-medium text-foreground">Regina Castro Fernández</p>
      <%= render Ui::TooltipComponent.new(text: "regina.castro.fernandez@lunaestudio.mx", side: :bottom, align: :start, truncated_only: true) do %>
        <p class="truncate rounded-sm font-mono text-2xs text-muted-foreground eui-focus" tabindex="0">regina.castro.fernandez@lunaestudio.mx</p>
      <% end %>
    </div>
    <span class="shrink-0 font-mono text-xs tabular-nums text-muted-foreground">4/10</span>
  </div>
</div>

Qué hace

  • Calentamiento compartido - el primero espera 400 ms; mientras uno está abierto, o medio segundo después, el siguiente sale ya y con menos recorrido.
  • Entra con el resorte snappy desde el lado del ancla (data-side) y crece desde --eui-origin; sale con una transición breve.
  • Popover manual en la capa superior - no lo recorta un overflow y no cierra los menús abiertos.
  • La flecha sigue apuntando al disparador aunque la burbuja se recorra para no salirse de la pantalla.

Props

Nombre Tipo Default Descripción
text String (obligatorio) Lo que dice el tooltip.
side :top | :bottom | :left | :right :top Lado preferido; se voltea si no cabe y entra desde ahí.
align :start | :center | :end :center Alineación sobre ese lado.
shortcut String | Array — Atajo en <kbd> mono ("⌘E" o ["⌘", "E"]). Solo visual: el atajo lo implementa la vista.
selector String (CSS) — Engancha el tooltip a otro elemento en vez de envolverlo; se busca primero cerca del componente.
delay Integer (ms) 400 Espera la primera vez; en caliente sale de inmediato.
arrow Boolean true Flecha que apunta al centro del disparador.
truncated_only Boolean false Solo aparece si el disparador está recortado (truncate).
content bloque — El disparador: su primer elemento (un botón, un enlace o un texto con tabindex=0).

Accesibilidad