Hover Card

For sighted users to preview content available behind a link.

Sides
In Dialog

Installation

Install the dependencies

pnpm add cn github:sveltery/base

Copy the component

Copy the following files into src/lib/components/ui/hover-card.

src/lib/components/ui/hover-card/HoverCardContent.svelte
<script lang="ts">
  import type { ComponentProps } from 'svelte';
  import { PreviewCard as Primitive } from '@sveltery/base/preview-card';
  import { cn } from 'cn';
  let {
    children,
    class: classProp,
    align = 'center',
    alignOffset = 4,
    side = 'bottom',
    sideOffset = 4,
    ...props
  }: ComponentProps<typeof Primitive.Popup> & Pick<ComponentProps<typeof Primitive.Positioner>, 'align' | 'alignOffset' | 'side' | 'sideOffset'> = $props();
</script>
<Primitive.Portal data-slot="hover-card-portal">
  <Primitive.Positioner {align} {alignOffset} {side} {sideOffset} class="isolate z-50">
    <Primitive.Popup data-slot="hover-card-content" {...props} class={cn('data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 ring-foreground/10 bg-popover text-popover-foreground w-64 rounded-lg p-2.5 text-sm shadow-md ring-1 duration-100 data-[side=inline-start]:slide-in-from-right-2 data-[side=inline-end]:slide-in-from-left-2 z-50 origin-(--transform-origin) outline-hidden', classProp)}>
      {@render children?.()}
    </Primitive.Popup>
  </Primitive.Positioner>
</Primitive.Portal>
src/lib/components/ui/hover-card/HoverCardTrigger.svelte
<script lang="ts">
  import type { ComponentProps } from 'svelte';
  import { PreviewCard as Primitive } from '@sveltery/base/preview-card';
  let { children, ...props }: ComponentProps<typeof Primitive.Trigger> = $props();
</script>
<Primitive.Trigger data-slot="hover-card-trigger" {children} {...props} />
src/lib/components/ui/hover-card/index.ts
import { PreviewCard as Primitive } from '@sveltery/base/preview-card';

export const HoverCard = Primitive.Root;
export { default as HoverCardTrigger } from './HoverCardTrigger.svelte';
export { default as HoverCardContent } from './HoverCardContent.svelte';

Update the import paths

Change the imports to match your project setup.

Usage

<script lang="ts">
  import { HoverCard, HoverCardContent, HoverCardTrigger } from '$lib/components/ui/hover-card';
</script>
<HoverCard>
  <HoverCardTrigger href="https://github.com/sveltery">@sveltery</HoverCardTrigger>
  <HoverCardContent>Svelte components on Base UI behavior.</HoverCardContent>
</HoverCard>

HoverCardTrigger renders a link. The card opens when the pointer rests on it or it gets keyboard focus, and it stays open while the pointer moves into the card. It is a preview for sighted users, so keep the link itself useful on its own.

Delays

delay and closeDelay on HoverCardTrigger set how long the card waits to open and to close, in milliseconds. They default to 600 and 300.

API reference

HoverCard is styled on top of the Sveltery Base PreviewCard primitive, which owns its behavior, positioning and accessibility. See the Base UI Preview Card documentation for the behavior it ports.