AvesraAvesrabeta

Tooltip

Displays informative text when users hover over or focus on an element. Apply the avTooltip directive to any host — no compound wrapper required. See also Popover for interactive anchored panels.

Import

import { AvTooltipDirective } from '@avesra/angular';

Usage

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-basic-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<button av-button variant="secondary" avTooltip="Tooltip content">Hover me</button>`,
})
export class TooltipBasicDemo {}

Anatomy

Tooltip is a directive-only API. The overlay (panel, text, and arrow) is created at runtime — no Trigger / Content / Arrow markup in your template.

<button
  av-button
  variant="secondary"
  avTooltip="Helpful information about this element"
>
  Hover for tooltip
</button>

<!-- Overlay is created at runtime with .av-tooltip, .av-tooltip__text, and [data-slot="overlay-arrow"] -->

Placement

Use tooltip-position for the preferred side — top (default), bottom, left, or right. The directive flips when the preferred side would overflow the viewport.

Hover
import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-positions-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="grid max-w-md grid-cols-3 gap-4">
      <div></div>
      <button
        av-button
        class="w-full"
        size="sm"
        variant="tertiary"
        avTooltip="Top placement"
        tooltip-position="top"
      >
        Top
      </button>
      <div></div>

      <button
        av-button
        class="w-full"
        size="sm"
        variant="tertiary"
        avTooltip="Left placement"
        tooltip-position="left"
      >
        Left
      </button>
      <div class="flex items-center justify-center text-xs text-muted">Hover</div>
      <button
        av-button
        class="w-full"
        size="sm"
        variant="tertiary"
        avTooltip="Right placement"
        tooltip-position="right"
      >
        Right
      </button>

      <div></div>
      <button
        av-button
        class="w-full"
        size="sm"
        variant="tertiary"
        avTooltip="Bottom placement"
        tooltip-position="bottom"
      >
        Bottom
      </button>
      <div></div>
    </div>`,
})
export class TooltipPositionsDemo {}

With Arrow

Avesra always renders an arrow on the overlay ([data-slot="overlay-arrow"]). There is no showArrow toggle — placement rotates the arrow automatically.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-basic-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<button av-button variant="secondary" avTooltip="Tooltip content">Hover me</button>`,
})
export class TooltipBasicDemo {}

Custom Triggers

Attach avTooltip to any hoverable or focusable element — icon buttons, avatars, chips, or custom hosts. Provide aria-label when the trigger has no visible text.

Jane DoeJDActive
import { Component } from '@angular/core';
import { AvAvatarImports, AvChipImports, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-custom-trigger-demo',
  imports: [AvAvatarImports, AvChipImports, AvTooltipDirective],
  template: `<div class="flex flex-wrap items-center gap-6">
      <span
        av-avatar
        size="sm"
        [avTooltip]="avatarTooltip"
        aria-label="User avatar"
        tabindex="0"
        [show-delay]="0"
      >
        <img av-avatar-image alt="Jane Doe" [src]="profileAvatarUrl" />
        <span av-avatar-fallback>JD</span>
      </span>

      <span
        av-chip
        color="success"
        variant="secondary"
        [avTooltip]="statusTooltip"
        aria-label="Status chip"
        tabindex="0"
        [show-delay]="0"
      >
        <svg class="size-3" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
          <path
            d="M8 1.5a6.5 6.5 0 1 0 0 13 6.5 6.5 0 0 0 0-13Zm3.78 4.72-4.25 4.25a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 1 1 1.06-1.06l1.47 1.47 3.72-3.72a.75.75 0 1 1 1.06 1.06Z"
          />
        </svg>
        <span av-chip-label>Active</span>
      </span>

      <div
        class="inline-flex cursor-default rounded-full bg-accent-soft p-2 text-accent-soft-foreground"
        [avTooltip]="helpTooltip"
        aria-label="Info icon"
        tabindex="0"
        tooltip-position="top"
        [show-delay]="0"
      >
        <svg
          class="size-4"
          viewBox="0 0 16 16"
          fill="none"
          stroke="currentColor"
          stroke-width="1.5"
          aria-hidden="true"
        >
          <circle cx="8" cy="8" r="6.25" />
          <path d="M8 7.25v4" stroke-linecap="round" />
          <circle cx="8" cy="5.25" r="0.75" fill="currentColor" stroke="none" />
        </svg>
      </div>
    </div>

    <ng-template #avatarTooltip>
      <div class="flex flex-col gap-0 py-1">
        <p class="font-semibold">Jane Doe</p>
        <p class="text-xs text-muted">jane&#64;example.com</p>
      </div>
    </ng-template>

    <ng-template #statusTooltip>
      <div class="flex items-center gap-1.5">
        <span class="relative flex size-2">
          <span
            class="absolute inline-flex h-full w-full animate-ping rounded-full bg-success opacity-75"
          ></span>
          <span class="relative inline-flex size-2 rounded-full bg-success"></span>
        </span>
        <p>Jane is currently online</p>
      </div>
    </ng-template>

    <ng-template #helpTooltip>
      <div class="max-w-xs px-1 py-1.5">
        <p class="mb-1 font-semibold">Help Information</p>
        <p class="text-sm text-muted">
          This is a helpful tooltip with more detailed information about this feature.
        </p>
      </div>
    </ng-template>`,
})
export class TooltipCustomTriggerDemo {
  readonly profileAvatarUrl = '/images/avatars/avatar-man-glasses-salt-pepper.png';
}

Events

Control how the tooltip opens with tooltip-event: hover (default), focus, or both.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-events-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="flex flex-wrap gap-3">
      <button av-button variant="outline" avTooltip="Opens on hover" tooltip-event="hover">
        Hover
      </button>
      <input
        class="w-44 rounded-xl border border-border bg-field px-3 py-2 text-sm text-foreground outline-none focus-visible:ring-2 focus-visible:ring-focus"
        type="text"
        placeholder="Focus me"
        avTooltip="Opens on focus"
        tooltip-event="focus"
      />
      <input
        class="w-44 rounded-xl border border-border bg-field px-3 py-2 text-sm text-foreground outline-none focus-visible:ring-2 focus-visible:ring-focus"
        type="text"
        placeholder="Hover or focus"
        avTooltip="Opens on hover and focus"
        tooltip-event="both"
      />
    </div>`,
})
export class TooltipEventsDemo {}

Delays

Tune timing with show-delay (default 700 ms) and hide-delay (default 0 ms).

import { Component } from '@angular/core';
import { AV_TOOLTIP_SHOW_DELAY_DEFAULT, AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-delays-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="flex flex-wrap gap-3">
      <button av-button variant="outline" avTooltip="Shows after 150ms" [show-delay]="150">
        Fast show (150ms)
      </button>
      <button av-button variant="outline" avTooltip="Uses default show delay" [show-delay]="showDelayDefault">
        Default show ({{ showDelayDefault }}ms)
      </button>
      <button
        av-button
        variant="outline"
        avTooltip="Stays visible 500ms after leave"
        [show-delay]="0"
        [hide-delay]="500"
      >
        Slow hide (500ms)
      </button>
      <button
        av-button
        variant="outline"
        avTooltip="1s show, 300ms hide"
        [show-delay]="1000"
        [hide-delay]="300"
      >
        Both delays
      </button>
    </div>`,
})
export class TooltipDelaysDemo {
  readonly showDelayDefault = AV_TOOLTIP_SHOW_DELAY_DEFAULT;
}

Auto Hide

When auto-hide is true (default), the tooltip dismisses when the pointer leaves the trigger. Set [auto-hide]="false" to keep it open while hovering the panel.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-auto-hide-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="flex flex-wrap gap-3">
      <button
        av-button
        variant="secondary"
        avTooltip="Default — hides when pointer leaves trigger"
        [auto-hide]="true"
      >
        auto-hide: true
      </button>
      <button
        av-button
        variant="secondary"
        avTooltip="Move pointer onto this tooltip before it closes"
        [auto-hide]="false"
        [show-delay]="0"
      >
        auto-hide: false
      </button>
    </div>`,
})
export class TooltipAutoHideDemo {}

Hide on Escape

Pressing Escape dismisses a visible tooltip by default. Set [hide-on-escape]="false" to disable that behavior.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-hide-on-escape-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="flex flex-wrap gap-3">
      <button av-button variant="outline" avTooltip="Press Escape to close" [show-delay]="0">
        hide-on-escape: true
      </button>
      <button
        av-button
        variant="outline"
        avTooltip="Escape will not close this tooltip"
        [show-delay]="0"
        [hide-on-escape]="false"
      >
        hide-on-escape: false
      </button>
    </div>`,
})
export class TooltipHideOnEscapeDemo {}

Position Offset

Nudge placement with position-top and position-left after the base alignment calculation.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-position-offset-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="flex flex-wrap gap-3">
      <button
        av-button
        variant="tertiary"
        avTooltip="Shifted 12px right and 8px down"
        tooltip-position="top"
        [position-left]="12"
        [position-top]="8"
        [show-delay]="0"
      >
        Custom offset
      </button>
      <button
        av-button
        variant="tertiary"
        avTooltip="Baseline top placement"
        tooltip-position="top"
        [show-delay]="0"
      >
        No offset
      </button>
    </div>`,
})
export class TooltipPositionOffsetDemo {}

Template Content

Bind [avTooltip] to an ng-template for formatted or multi-line content.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-template-content-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<button
      av-button
      variant="primary"
      [avTooltip]="richTooltip"
      tooltip-position="bottom"
      [show-delay]="0"
    >
      Rich tooltip
    </button>
    <ng-template #richTooltip>
      <div class="flex flex-col gap-1">
        <span class="font-medium">Avesra Tooltip</span>
        <span class="text-muted">TemplateRef with custom layout</span>
      </div>
    </ng-template>`,
})
export class TooltipTemplateContentDemo {}

Dynamic Content

Tooltip text can be driven by component state — the overlay reflects the latest content on the next show cycle.

Clicks: 0
import { Component, signal } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-dynamic-content-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="flex flex-wrap items-center gap-3">
      <button
        av-button
        variant="secondary"
        [avTooltip]="dynamicLabel()"
        [show-delay]="0"
      >
        Dynamic trigger
      </button>
      <button av-button variant="outline" (click)="updateDynamicTooltip()">
        Update tooltip text
      </button>
      <span class="text-sm text-muted">Clicks: {{ clicks() }}</span>
    </div>`,
})
export class TooltipDynamicContentDemo {
  readonly dynamicLabel = signal('Hover to see dynamic content');
  readonly clicks = signal(0);

  updateDynamicTooltip(): void {
    this.clicks.update((value) => value + 1);
    this.dynamicLabel.set(`Clicked ${this.clicks()} time(s)`);
  }
}

Long Text

Long labels wrap within the panel's max-w-xs constraint.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-long-text-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<button
      av-button
      variant="outline"
      avTooltip="This is a longer tooltip message that demonstrates wrapping behavior when the advisory text exceeds the maximum width of the overlay surface."
      [show-delay]="0"
    >
      Long tooltip
    </button>`,
})
export class TooltipLongTextDemo {}

Disabled

Set [tooltip-disabled]="true" to suppress the tooltip without removing the directive.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-disabled-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<button av-button variant="secondary" avTooltip="You should not see this" tooltip-disabled>
      Disabled tooltip
    </button>`,
})
export class TooltipDisabledDemo {}

Viewport Flip

When the preferred position would clip outside the viewport, the tooltip tries fallback sides automatically.

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-viewport-flip-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `<div class="flex justify-end">
      <button
        av-button
        size="sm"
        variant="tertiary"
        avTooltip="Requested right, may flip near the viewport edge"
        tooltip-position="right"
        [show-delay]="0"
      >
        Near right edge
      </button>
    </div>`,
})
export class TooltipViewportFlipDemo {}

Customization

Tailwind CSS

Style the trigger host with utilities. Overlay appearance is customized via Global CSS on .av-tooltip (there is no content component className).

import { Component } from '@angular/core';
import { AvButtonComponent, AvTooltipDirective } from '@avesra/angular';

@Component({
  selector: 'app-tooltip-custom-styles-demo',
  imports: [AvButtonComponent, AvTooltipDirective],
  template: `
<button
  av-button
  class="cursor-help"
  variant="secondary"
  avTooltip="Copied to clipboard"
  [show-delay]="0"
>
  Share link
</button>
`,
})
export class TooltipCustomStylesDemo {}

Global CSS

To customize Tooltip classes, use the @layer components directive.

@layer components {
  .av-tooltip {
    @apply rounded-xl shadow-lg;
  }

  .av-tooltip__text {
    @apply text-xs;
  }
}

Styling Reference

Avesra follows the BEM methodology so variants and states stay reusable and easy to customize.

CSS Classes

Base Classes

  • .av-tooltip — Base tooltip overlay with animations
  • .av-tooltip__text — Content wrapper above the arrow
  • [data-slot="overlay-arrow"] — Arrow SVG

Interactive States

  • Entering: [data-entering] — applied during appearance
  • Exiting: [data-exiting] — applied during disappearance
  • Placement: [data-placement="*"] — drives arrow rotation and slide direction

API Reference

All options are inputs on the [avTooltip] directive host. The overlay is not a separate Angular component — there are no Trigger / Content / Arrow APIs.

PropTypeDefaultDescription
avTooltipstring | TemplateRef<unknown>—Tooltip content. Plain text or a template ([avTooltip]).
tooltip-position'top' | 'bottom' | 'left' | 'right''top'Preferred tooltip position ([avTooltip]).
tooltip-event'hover' | 'focus' | 'both''hover'Event that opens the tooltip ([avTooltip]).
show-delaynumber700Delay before showing the tooltip in milliseconds ([avTooltip]).
hide-delaynumber0Delay before hiding the tooltip in milliseconds ([avTooltip]).
tooltip-disabledbooleanfalseDisables the tooltip ([avTooltip]).
auto-hidebooleantrueHides the tooltip when the pointer leaves the trigger ([avTooltip]).
hide-on-escapebooleantrueHides the tooltip when Escape is pressed ([avTooltip]).
position-topnumber0Additional vertical offset in pixels ([avTooltip]).
position-leftnumber0Additional horizontal offset in pixels ([avTooltip]).

Prefer concise labels; use Popover when content requires interaction. While visible, the trigger receives aria-describedby pointing at the tooltip.

Made with ❤ by SyntaxHertz. Open source and available on GitHub.