Skip to content
Ndop UI

Hover Card

A preview of what's behind a link, shown to pointer and keyboard users while they hover or focus it.

import { Component } from '@angular/core';
import { Avatar, AvatarFallback } from '@/ui/avatar/avatar';
import { Button } from '@/ui/button/button';
import {
  HoverCardContent,
  HoverCard,
  HoverCardPortal,
  HoverCardTrigger,
} from '@/ui/hover-card/hover-card';

/**
 * A profile preview behind a username link. The link says where it goes
 * on its own; the card adds a glance for sighted users.
 */
@Component({
  selector: 'example-hover-card-demo',
  imports: [
    Avatar,
    AvatarFallback,
    Button,
    HoverCardContent,
    HoverCard,
    HoverCardPortal,
    HoverCardTrigger,
  ],
  template: `
    <ng-container ndHoverCard>
      <a ndButton variant="link" ndHoverCardTrigger href="#angular">
        &#64;angular
      </a>
      <ng-template ndHoverCardPortal>
        <div ndHoverCardContent class="w-80">
          <div class="flex justify-between gap-4">
            <span ndAvatar><span ndAvatarFallback>NG</span></span>
            <div class="space-y-1">
              <p class="text-sm font-semibold">&#64;angular</p>
              <p class="text-sm">
                The web development framework for building modern apps.
              </p>
              <p class="text-xs text-muted-foreground">Joined September 2016</p>
            </div>
          </div>
        </div>
      </ng-template>
    </ng-container>
  `,
})
export class HoverCardDemoExample {}

A hover card previews what's behind a link, such as a person's profile behind their username, for sighted users: it opens after a short delay while the link is hovered or focused with the keyboard, and stays open while the pointer is over it. Screen readers don't announce it, so the link must make sense without it.

Installation

npx @ndop/cli add hover-card

Usage

import { Avatar, AvatarFallback } from '@/ui/avatar/avatar';
import { Button } from '@/ui/button/button';
import {
  HoverCardContent,
  HoverCard,
  HoverCardPortal,
  HoverCardTrigger,
} from '@/ui/hover-card/hover-card';
<ng-container ndHoverCard>
  <a ndButton variant="link" ndHoverCardTrigger href="#angular">
    &#64;angular
  </a>
  <ng-template ndHoverCardPortal>
    <div ndHoverCardContent class="w-80">
      <div class="flex justify-between gap-4">
        <span ndAvatar><span ndAvatarFallback>NG</span></span>
        <div class="space-y-1">
          <p class="text-sm font-semibold">&#64;angular</p>
          <p class="text-sm">
            The web development framework for building modern apps.
          </p>
          <p class="text-xs text-muted-foreground">Joined September 2016</p>
        </div>
      </div>
    </div>
  </ng-template>
</ng-container>

Examples

Hover Card sides

Cards on each side of their links.

import { Component } from '@angular/core';
import { Button } from '@/ui/button/button';
import {
  HoverCardContent,
  HoverCard,
  HoverCardPortal,
  HoverCardTrigger,
} from '@/ui/hover-card/hover-card';

/**
 * Cards on each side of their links; they flip when there's no room.
 * `start` and `end` swap in right-to-left layouts.
 */
@Component({
  selector: 'example-hover-card-sides',
  imports: [
    Button,
    HoverCardContent,
    HoverCard,
    HoverCardPortal,
    HoverCardTrigger,
  ],
  template: `
    <div class="flex flex-wrap gap-2">
      @for (side of sides; track side) {
        <ng-container ndHoverCard>
          <a
            ndButton
            variant="outline"
            ndHoverCardTrigger
            class="capitalize"
            [href]="'#' + side"
          >
            {{ side }} side
          </a>
          <ng-template ndHoverCardPortal>
            <div ndHoverCardContent [side]="side">
              <p class="text-sm">This card opens on the {{ side }} side.</p>
            </div>
          </ng-template>
        </ng-container>
      }
    </div>
  `,
})
export class HoverCardSidesExample {
  protected readonly sides = ['top', 'end', 'bottom', 'start'] as const;
}

Accessibility

Implements the Tooltip pattern of the WAI-ARIA Authoring Practices.

Keyboard

Keys the component handles.

Key Action
TabFocusing the trigger shows the tooltip; moving focus away hides it.
EscapeHides the tooltip without moving focus.
TabFocusing the trigger opens the card after the delay; leaving it closes it.
EscapeCloses the card without moving focus.

Focus

  • The tooltip never takes focus; focus stays on the trigger.
  • The card never takes focus; focus stays on the trigger.

Right-to-left

As in Popover's spec.md: start and end sides and alignments swap in RTL. start and end sides are logical and swap in right-to-left layouts.

API reference

HoverCard

[ndHoverCard]

A hover card (@ndop/primitives/hover-card): a preview of what's behind a link, for sighted users, after a short delay on hover or keyboard focus. Put it on an ng-container.

HoverCard binding Type Default
[defaultOpen]
Initial open state when open is not bound.
booleanfalse
[openDelay]
Delay before opening on hover, in milliseconds.
number700
[closeDelay]
Delay before closing once the pointer has left, in milliseconds.
number300
[(open)]
Whether the tooltip is open. Two-way bindable with [(open)].
booleanfalse

HoverCardTrigger

[ndHoverCardTrigger]

The link its card previews.

Data attribute Values
data-slothover-card-trigger
data-state —

HoverCardPortal

ng-template[ndHoverCardPortal]

Marks the ng-template its card renders while open.

HoverCardContent

[ndHoverCardContent]

A hover card (@ndop/primitives/hover-card): a preview of what's behind a link, shown after a short delay while the link is hovered or focused with the keyboard, and kept open while the pointer is over it. It's for sighted users only, so the link must make sense without it.

HoverCardContent binding Type Default
[class]
Extra classes, merged last so they win.
string''
[side]
Side of the trigger it sits on, before flipping.
'bottom' | 'top' | 'start' | 'end''bottom'
[align]
Alignment along that side.
'start' | 'end' | 'center''center'
[sideOffset]
Gap between the trigger and the card, in pixels.
number4
Data attribute Values
data-slothover-card-content
data-state —
data-side —
data-align —

Agent notes

Do

  • Use it for a preview of something people can also open.
  • Keep it short: a name, a line of text, a date.

Don't

  • Don't put anything only in the card: what it says must be on the page it previews.
  • Don't put buttons or forms in it; use a Popover.