ATOM
Components (registry)Surfaces

Tooltip

Hover or focus and a short hint appears beside the control. Labels for icon-only actions.

Preview

Editorial

<!-- F12c editorial — non-derivable only. Review: Karen. -->

## Ejemplos

```html <!-- CSS-only: casos simples, un trigger, cero JS --> <button data-tooltip="Guardar cambios" data-tooltip-dir="top">Guardar</button> ```

```html <!-- Smart (behavior tooltip.ts): grupos que viajan con Flip, edge detection --> <div data-tooltip-smart data-tooltip-placement="top" data-tooltip-delay="0" data-tooltip-hide-delay="100"> <button data-tooltip-trigger data-tooltip-group="filters" data-tooltip-content="Filtra por color">Color</button> <button data-tooltip-trigger data-tooltip-group="filters" data-tooltip-content="Solo ofertas">Sale</button> </div> <!-- + initTooltipSmart() de @atom-uikit/animations (AtomMotion.initAll() lo cubre) --> ```

## Cuál de los dos

| Caso | Mecanismo | |---|---| | Un hint sobre un icono o botón suelto | **CSS-only** (`data-tooltip`) — cero JS, cero costo | | Fila de filtros/acciones donde el tooltip debe VIAJAR entre triggers | **Smart** (`data-tooltip-smart`) — Flip por grupo | | Trigger pegado al borde del viewport | **Smart** — detecta bordes y se voltea solo | | Necesitas delay de aparición o de salida | **Smart** (`data-tooltip-delay` / `data-tooltip-hide-delay`, en ms) |

## Accesibilidad

- El smart es **funcional** bajo `prefers-reduced-motion`: aparece y desaparece instantáneo (el original del que se adaptó lo apagaba por completo). - `aria-describedby` real del trigger al popup, y **Escape** cierra sin mover el puntero (WCAG 1.4.13). Focus/blur equivalen a hover para teclado. - El popup del smart usa `--primary`/`--primary-foreground`: inverso clásico y theme-aware — en dark el CSS-only (neutral-900 fijo) contrasta peor; para superficies dark prefiere el smart o la variante `data-tooltip-theme="light"`.

## Cuándo no usar

- Contenido interactivo (links, botones dentro del popup) → usa `Popover`/`DropdownMenu`; un tooltip es solo lectura y `pointer-events: none`. - Información imprescindible para completar la tarea → ponla visible; el hover no existe en táctil. - Textos largos o multilínea en desktop → usa un `Dialog` o texto inline.

Anatomía CSS

<div class="is-bottom">

</div>
ClasePropósito
is-bottomroot

Tokens resueltos

Valores finales tras seguir la cadena de tokens. Derivados del source: si un token cambia, esta tabla cambia sola.

VariantePropValorToken
allbg#0a0a0aprimary
allfg#fafafaprimary.foreground

Animaciones

PropiedadDuraciónEasing
opacity0.25scubic-bezier(0.625
0.05
0
1)
transform0.25scubic-bezier(0.625
0.05
0
1)

CSS mínimo funcional

Autocontenido: sin imports ni tokens. Para previews y prototipos — en producción se consume el CSS del DS (@atom-uikit/css/components.css</code>, <code>@atom-uikit/tokens/tokens.css).

/* -------------------------------------------------------------------------
   Tooltip (CSS Only)

   Small text hint on hover. Attach to any element.
   Direction: top (default), bottom, left, right
   Theme: dark (default), light

   Usage: wrap trigger in [data-tooltip] with content as attribute value.
   <button data-tooltip="Save changes" data-tooltip-dir="top">Save</button>
   ------------------------------------------------------------------------- */

[data-tooltip] {
  position: relative;
}

[data-tooltip]::before,
[data-tooltip]::after {
  position: absolute;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 0.25s cubic-bezier(0.625, 0.05, 0, 1),
    transform 0.25s cubic-bezier(0.625, 0.05, 0, 1);
  z-index: 70;
}

/* Text bubble */
[data-tooltip]::before {
  content: attr(data-tooltip);
  padding: 4px 8px;
  border-radius: 6px;
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif, ui-sans-serif, system-ui, sans-serif;
  font-size: 10.24px;
  font-weight: 500;
  line-height: 1.4;
  white-space: nowrap;
  max-width: 200px;
}

/* Arrow */
[data-tooltip]::after {
  content: '';
  width: 6px;
  height: 6px;
  transform: rotate(45deg);
}

/* ---- Dark theme (default) ---- */

[data-tooltip]::before {
  background-color: #171717;
  color: #fafafa;
}

[data-tooltip]::after {
  background-color: #171717;
}

/* ---- Light theme ---- */

[data-tooltip-theme="light"]::before {
  background-color: #fafafa;
  color: #0a0a0a;
  border: 1px solid #e5e5e5;
  box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
}

[data-tooltip-theme="light"]::after {
  background-color: #fafafa;
  border: 1px solid #e5e5e5;
  border-top: none;
  border-left: none;
}

/* ---- Show on hover ---- */

[data-tooltip]:hover::before,
[data-tooltip]:hover::after {
  opacity: 1;
  visibility: visible;
}

/* ---- Top (default) ---- */

[data-tooltip]::before,
[data-tooltip-dir="top"]::before {
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
}

[data-tooltip]:hover::before,
[data-tooltip-dir="top"]:hover::before {
  transform: translateX(-50%) translateY(0);
}

[data-tooltip]::after,
[data-tooltip-dir="top"]::after {
  bottom: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
}

/* ---- Bottom ---- */

[data-tooltip-dir="bottom"]::before {
  bottom: auto;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
}

[data-tooltip-dir="bottom"]:hover::before {
  transform: translateX(-50%) translateY(0);
}

[data-tooltip-dir="bottom"]::after {
  bottom: auto;
  top: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%) rotate(225deg);
}

/* ---- Left ---- */

[data-tooltip-dir="left"]::before {
  bottom: auto;
  top: 50%;
  right: calc(100% + 8px);
  left: auto;
  transform: translateY(-50%) translateX(4px);
}

[data-tooltip-dir="left"]:hover::before {
  transform: translateY(-50%) translateX(0);
}

[data-tooltip-dir="left"]::after {
  bottom: auto;
  top: 50%;
  right: calc(100% + 5px);
  left: auto;
  transform: translateY(-50%) rotate(315deg);
}

/* ---- Right ---- */

[data-tooltip-dir="right"]::before {
  bottom: auto;
  top: 50%;
  left: calc(100% + 8px);
  transform: translateY(-50%) translateX(-4px);
}

[data-tooltip-dir="right"]:hover::before {
  transform: translateY(-50%) translateX(0);
}

[data-tooltip-dir="right"]::after {
  bottom: auto;
  top: 50%;
  left: calc(100% + 5px);
  transform: translateY(-50%) rotate(135deg);
}

/* ---- Reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
  [data-tooltip]::before,
  [data-tooltip]::after {
    transition-duration: 0ms;
  }
}

/* -------------------------------------------------------------------------
   Smart tooltip (behavior tooltip.ts) — popup real en portal

   El popup lo crea el behavior en runtime (por eso el JS le pone la clase en
   ambas variantes, con y sin ds-). Edge detection, delays y el viaje con
   Flip entre triggers del mismo grupo viven en
   packages/animations/src/tooltip.ts; aqui SOLO pintura.

   Colores: --primary/--primary-foreground = el inverso clasico de tooltip,
   theme-aware gratis (oscuro sobre light, claro sobre dark) — mejora el
   contraste en dark frente al CSS-only de arriba (neutral-900 fijo).
   ------------------------------------------------------------------------- */

.tooltip__popup {
  position: fixed;
  z-index: 70;
  pointer-events: none;
  will-change: transform, opacity;
}

.tooltip__popup-bg {
  position: absolute;
  inset: 0;
  background-color: #0a0a0a;
  border-radius: 8px;
  will-change: transform;
}

.tooltip__popup-text {
  position: relative;
  z-index: 1;
  padding: 4px 8px;
  color: #fafafa;
  font-family: 'inter tight', -apple-system, blinkmacsystemfont, 'segoe ui', roboto, helvetica, arial, sans-serif, ui-sans-serif, system-ui, sans-serif;
  font-size: 10.24px;
  font-weight: 500;
  line-height: 1.4;
  white-space: nowrap;
}

/* transform-origin por placement: la entrada escala DESDE el trigger */
.tooltip__popup.is-top {
  transform-origin: bottom center;
}
.tooltip__popup.is-bottom {
  transform-origin: top center;
}
.tooltip__popup.is-left {
  transform-origin: right center;
}
.tooltip__popup.is-right {
  transform-origin: left center;
}

@media (max-width: 479px) {
  .tooltip__popup-text {
    max-width: calc(100vw - 32px);
    white-space: normal;
  }
}

Codigo fuente

styles/components/tooltip.css
/* -------------------------------------------------------------------------
   Tooltip (CSS Only)

   Small text hint on hover. Attach to any element.
   Direction: top (default), bottom, left, right
   Theme: dark (default), light

   Usage: wrap trigger in [data-tooltip] with content as attribute value.
   <button data-tooltip="Save changes" data-tooltip-dir="top">Save</button>
   ------------------------------------------------------------------------- */

[data-tooltip] {
  position: relative;
}

[data-tooltip]::before,
[data-tooltip]::after {
  position: absolute;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 0.25s cubic-bezier(0.625, 0.05, 0, 1),
    transform 0.25s cubic-bezier(0.625, 0.05, 0, 1);
  z-index: var(--z-index-70, 70);
}

/* Text bubble */
[data-tooltip]::before {
  content: attr(data-tooltip);
  padding: 4px 8px;
  border-radius: 6px;
  font-family: var(--font-family-sans), ui-sans-serif, system-ui, sans-serif;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  white-space: nowrap;
  max-width: 200px;
}

/* Arrow */
[data-tooltip]::after {
  content: '';
  width: 6px;
  height: 6px;
  transform: rotate(45deg);
}

/* ---- Dark theme (default) ---- */

[data-tooltip]::before {
  background-color: var(--color-neutral-900);
  color: var(--color-neutral-50);
}

[data-tooltip]::after {
  background-color: var(--color-neutral-900);
}

/* ---- Light theme ---- */

[data-tooltip-theme="light"]::before {
  background-color: var(--background);
  color: var(--foreground);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-sm);
}

[data-tooltip-theme="light"]::after {
  background-color: var(--background);
  border: 1px solid var(--border);
  border-top: none;
  border-left: none;
}

/* ---- Show on hover ---- */

[data-tooltip]:hover::before,
[data-tooltip]:hover::after {
  opacity: 1;
  visibility: visible;
}

/* ---- Top (default) ---- */

[data-tooltip]::before,
[data-tooltip-dir="top"]::before {
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(4px);
}

[data-tooltip]:hover::before,
[data-tooltip-dir="top"]:hover::before {
  transform: translateX(-50%) translateY(0);
}

[data-tooltip]::after,
[data-tooltip-dir="top"]::after {
  bottom: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
}

/* ---- Bottom ---- */

[data-tooltip-dir="bottom"]::before {
  bottom: auto;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%) translateY(-4px);
}

[data-tooltip-dir="bottom"]:hover::before {
  transform: translateX(-50%) translateY(0);
}

[data-tooltip-dir="bottom"]::after {
  bottom: auto;
  top: calc(100% + 5px);
  left: 50%;
  transform: translateX(-50%) rotate(225deg);
}

/* ---- Left ---- */

[data-tooltip-dir="left"]::before {
  bottom: auto;
  top: 50%;
  right: calc(100% + 8px);
  left: auto;
  transform: translateY(-50%) translateX(4px);
}

[data-tooltip-dir="left"]:hover::before {
  transform: translateY(-50%) translateX(0);
}

[data-tooltip-dir="left"]::after {
  bottom: auto;
  top: 50%;
  right: calc(100% + 5px);
  left: auto;
  transform: translateY(-50%) rotate(315deg);
}

/* ---- Right ---- */

[data-tooltip-dir="right"]::before {
  bottom: auto;
  top: 50%;
  left: calc(100% + 8px);
  transform: translateY(-50%) translateX(-4px);
}

[data-tooltip-dir="right"]:hover::before {
  transform: translateY(-50%) translateX(0);
}

[data-tooltip-dir="right"]::after {
  bottom: auto;
  top: 50%;
  left: calc(100% + 5px);
  transform: translateY(-50%) rotate(135deg);
}

/* ---- Reduced motion ---- */

@media (prefers-reduced-motion: reduce) {
  [data-tooltip]::before,
  [data-tooltip]::after {
    transition-duration: 0ms;
  }
}

/* -------------------------------------------------------------------------
   Smart tooltip (behavior tooltip.ts) — popup real en portal

   El popup lo crea el behavior en runtime (por eso el JS le pone la clase en
   ambas variantes, con y sin ds-). Edge detection, delays y el viaje con
   Flip entre triggers del mismo grupo viven en
   packages/animations/src/tooltip.ts; aqui SOLO pintura.

   Colores: --primary/--primary-foreground = el inverso clasico de tooltip,
   theme-aware gratis (oscuro sobre light, claro sobre dark) — mejora el
   contraste en dark frente al CSS-only de arriba (neutral-900 fijo).
   ------------------------------------------------------------------------- */

.tooltip__popup {
  position: fixed;
  z-index: var(--z-index-70, 70);
  pointer-events: none;
  will-change: transform, opacity;
}

.tooltip__popup-bg {
  position: absolute;
  inset: 0;
  background-color: var(--primary);
  border-radius: var(--radius-md);
  will-change: transform;
}

.tooltip__popup-text {
  position: relative;
  z-index: 1;
  padding: var(--spacing-1) var(--spacing-2);
  color: var(--primary-foreground);
  font-family: var(--font-family-sans), ui-sans-serif, system-ui, sans-serif;
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  white-space: nowrap;
}

/* transform-origin por placement: la entrada escala DESDE el trigger */
.tooltip__popup.is-top {
  transform-origin: bottom center;
}
.tooltip__popup.is-bottom {
  transform-origin: top center;
}
.tooltip__popup.is-left {
  transform-origin: right center;
}
.tooltip__popup.is-right {
  transform-origin: left center;
}

@media (max-width: 479px) {
  .tooltip__popup-text {
    max-width: calc(100vw - var(--spacing-8));
    white-space: normal;
  }
}

On this page

Detalles

Publicado14 de mayo de 2026
Categoriasurfaces
Lectura...
Visitas...
Ayuda?Slack

Componente

Tooltip

Source

components-react / cssDisponible via MCP: atom_uikit_source("tooltip")