# Hover Card

<div class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">
<HoverCard delay={10} closeDelay={100}> <HoverCardTrigger asChild> <Button variant="link">{"@data-slot"}</Button> </HoverCardTrigger> <HoverCardContent class="flex w-64 flex-col gap-0.5"> <div class="font-semibold">{"@data-slot"}</div> <div>{"Headless UI components for vanilla JavaScript."}</div> <div class="text-muted-foreground mt-1 text-xs">{" Created by Bejamas in 2026 "}</div> </HoverCardContent> </HoverCard>
</div>

```astro
---
---

<HoverCard delay={10} closeDelay={100}>
  <HoverCardTrigger asChild>
    <Button variant="link">@data-slot</Button>
  </HoverCardTrigger>
  <HoverCardContent class="flex w-64 flex-col gap-0.5">
    <div class="font-semibold">@data-slot</div>
    <div>Headless UI components for vanilla JavaScript.</div>
    <div class="text-muted-foreground mt-1 text-xs">
      Created by Bejamas in 2026
    </div>
  </HoverCardContent>
</HoverCard>
```

## Installation

<DocsTabs syncKey="pkg">
  <DocsTabItem label="bun">
  ```bash
  bunx bejamas add hover-card
  ```
  </DocsTabItem>
  <DocsTabItem label="npm">
  ```bash
  npx bejamas add hover-card
  ```
  </DocsTabItem>
  <DocsTabItem label="pnpm">
  ```bash
  pnpm dlx bejamas add hover-card
  ```
  </DocsTabItem>
  <DocsTabItem label="yarn">
  ```bash
  yarn dlx bejamas add hover-card
  ```
  </DocsTabItem>
</DocsTabs>

## Usage

```astro nocollapse
---
---

<HoverCard>
  <HoverCardTrigger asChild>
    <Button variant="link">Hover Here</Button>
  </HoverCardTrigger>
  <HoverCardContent>
    <div class="font-semibold">@nextjs</div>
    <div>The React Framework - created and maintained by @vercel.</div>
  </HoverCardContent>
</HoverCard>
```

## Props

| Prop | Type | Default |
|---|---|---|
| <code>class</code> | `string` | `""` |
| <code>defaultOpen</code> | `boolean` | `false` |
| <code>delay</code> | `number` | `700` |
| <code>skipDelayDuration</code> | `number` | `300` |
| <code>closeDelay</code> | `number` | `300` |
| <code>closeOnClickOutside</code> | `boolean` | `true` |
| <code>closeOnEscape</code> | `boolean` | `true` |

## Examples

### With Avatar

<div class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">
<HoverCard> <HoverCardTrigger asChild> <Button variant="link">@thom</Button> </HoverCardTrigger> <HoverCardContent class="flex w-72 gap-3 p-3"> <Avatar class="size-10 rounded-full"> <AvatarImage> <img src="https://github.com/thomkrupa.png" alt="@thom" /> </AvatarImage> <AvatarFallback>TK</AvatarFallback> </Avatar> <div class="space-y-1"> <div class="text-sm font-semibold leading-none">@thom</div> <p class="text-muted-foreground text-sm"> Building design systems and UI tooling. </p> </div> </HoverCardContent> </HoverCard>
</div>

```astro
---
---

<HoverCard>
  <HoverCardTrigger asChild>
    <Button variant="link">@thom</Button>
  </HoverCardTrigger>
  <HoverCardContent class="flex w-72 gap-3 p-3">
    <Avatar class="size-10 rounded-full">
      <AvatarImage>
        <img src="https://github.com/thomkrupa.png" alt="@thom" />
      </AvatarImage>
      <AvatarFallback>TK</AvatarFallback>
    </Avatar>
    <div class="space-y-1">
      <div class="text-sm font-semibold leading-none">@thom</div>
      <p class="text-muted-foreground text-sm">
        Building design systems and UI tooling.
      </p>
    </div>
  </HoverCardContent>
</HoverCard>
```

### Different sides

<div class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">
<div class="flex flex-wrap items-center gap-3 py-8"> <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}> <HoverCardTrigger asChild> <Button variant="outline">{"Left"}</Button> </HoverCardTrigger> <HoverCardContent side="left">{"Appears on left."}</HoverCardContent> </HoverCard> <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}> <HoverCardTrigger asChild> <Button variant="outline">{"Top"}</Button> </HoverCardTrigger> <HoverCardContent side="top">{"Appears on top."}</HoverCardContent> </HoverCard> <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}> <HoverCardTrigger asChild> <Button variant="outline">{"Bottom"}</Button> </HoverCardTrigger> <HoverCardContent side="bottom">{"Appears on bottom."}</HoverCardContent> </HoverCard> <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}> <HoverCardTrigger asChild> <Button variant="outline">{"Right"}</Button> </HoverCardTrigger> <HoverCardContent side="right">{"Appears on right."}</HoverCardContent> </HoverCard> </div>
</div>

```astro
---
---

<div class="flex flex-wrap items-center gap-3 py-8">
  <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}>
    <HoverCardTrigger asChild>
      <Button variant="outline">Left</Button>
    </HoverCardTrigger>
    <HoverCardContent side="left">Appears on left.</HoverCardContent>
  </HoverCard>
  <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}>
    <HoverCardTrigger asChild>
      <Button variant="outline">Top</Button>
    </HoverCardTrigger>
    <HoverCardContent side="top">Appears on top.</HoverCardContent>
  </HoverCard>
  <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}>
    <HoverCardTrigger asChild>
      <Button variant="outline">Bottom</Button>
    </HoverCardTrigger>
    <HoverCardContent side="bottom">Appears on bottom.</HoverCardContent>
  </HoverCard>
  <HoverCard delay={10} closeDelay={80} skipDelayDuration={0}>
    <HoverCardTrigger asChild>
      <Button variant="outline">Right</Button>
    </HoverCardTrigger>
    <HoverCardContent side="right">Appears on right.</HoverCardContent>
  </HoverCard>
</div>
```

### Positioning options

Configure placement options on `HoverCardContent`:

<div class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">
<HoverCard> <HoverCardTrigger asChild> <Button variant="outline">{"Custom positioning"}</Button> </HoverCardTrigger> <HoverCardContent side="top" sideOffset={8} align="start" >{" Uses content-level positioning options. "}</HoverCardContent> </HoverCard>
</div>

```astro
---
---

<HoverCard>
  <HoverCardTrigger asChild>
    <Button variant="outline">Custom positioning</Button>
  </HoverCardTrigger>
  <HoverCardContent
    side="top"
    sideOffset={8}
    align="start"
  >
    Uses content-level positioning options.
  </HoverCardContent>
</HoverCard>
```

### Custom timing

<div class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">
<HoverCard delay={600} closeDelay={200} skipDelayDuration={300}> <HoverCardTrigger asChild> <Button variant="outline">{"Hover with delay"}</Button> </HoverCardTrigger> <HoverCardContent>{" Opens after 600ms, closes after 200ms. "}</HoverCardContent> </HoverCard>
</div>

```astro
---
---

<HoverCard delay={600} closeDelay={200} skipDelayDuration={300}>
  <HoverCardTrigger asChild>
    <Button variant="outline">Hover with delay</Button>
  </HoverCardTrigger>
  <HoverCardContent>
    Opens after 600ms, closes after 200ms.
  </HoverCardContent>
</HoverCard>
```

## API Reference

### Events

The hover card emits custom events that you can listen to:

| Event | Detail | Description |
|-------|--------|-------------|
| `hover-card:change` | `{ open: boolean, reason: string, trigger: HTMLElement, content: HTMLElement }` | Fired when visibility changes |

<div id="sl-bejamas-console-preview-1" class="not-content sl-bejamas-component-preview flex justify-center px-4 md:px-10 py-12 border border-border rounded-t-lg min-h-72 items-center">
<HoverCard id="my-hover-card"> <HoverCardTrigger asChild> <Button variant="outline">{"Hover me"}</Button> </HoverCardTrigger> <HoverCardContent>{" Hover card content "}</HoverCardContent> </HoverCard>
</div>
<div class="not-content sl-bejamas-console-log-shell px-4 md:px-10 py-4 border border-border border-t-0">
<pre id="sl-bejamas-console-preview-1-output" data-slot="event-log" class="sl-bejamas-console-log w-full p-3 rounded-md bg-muted text-xs font-mono text-muted-foreground min-h-[80px] max-h-[200px] overflow-y-auto">Waiting for logs...</pre>
<script type="module" src="data:text/javascript;charset=utf-8,(function%20()%20%7B%0A%20%20var%20root%20%3D%20document.getElementById(%22sl-bejamas-console-preview-1%22)%3B%0A%20%20var%20panel%20%3D%20document.getElementById(%22sl-bejamas-console-preview-1-output%22)%3B%0A%20%20if%20(!root%20%7C%7C%20!panel)%20return%3B%0A%0A%20%20var%20placeholder%20%3D%20panel.textContent%20%7C%7C%20%22Waiting%20for%20logs...%22%3B%0A%20%20var%20hasLogs%20%3D%20false%3B%0A%0A%20%20var%20toText%20%3D%20function%20(value)%20%7B%0A%20%20%20%20if%20(typeof%20value%20%3D%3D%3D%20%22string%22)%20return%20value%3B%0A%20%20%20%20if%20(value%20%3D%3D%3D%20undefined)%20return%20%22undefined%22%3B%0A%20%20%20%20if%20(value%20%3D%3D%3D%20null)%20return%20%22null%22%3B%0A%20%20%20%20try%20%7B%0A%20%20%20%20%20%20return%20JSON.stringify(value)%3B%0A%20%20%20%20%7D%20catch%20(_error)%20%7B%0A%20%20%20%20%20%20return%20String(value)%3B%0A%20%20%20%20%7D%0A%20%20%7D%3B%0A%0A%20%20var%20append%20%3D%20function%20(level%2C%20args)%20%7B%0A%20%20%20%20var%20stamp%20%3D%20new%20Date().toLocaleTimeString()%3B%0A%20%20%20%20var%20body%20%3D%20Array.prototype.map.call(args%2C%20toText).join(%22%20%22)%3B%0A%20%20%20%20var%20line%20%3D%20%22%5B%22%20%2B%20stamp%20%2B%20%22%5D%20%22%20%2B%20String(level).toUpperCase()%20%2B%20%22%3A%20%22%20%2B%20body%3B%0A%20%20%20%20var%20current%20%3D%20panel.textContent%20%7C%7C%20%22%22%3B%0A%20%20%20%20if%20(!hasLogs%20%26%26%20current.trim()%20%3D%3D%3D%20placeholder.trim())%20%7B%0A%20%20%20%20%20%20panel.textContent%20%3D%20line%3B%0A%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20panel.textContent%20%3D%20current%20%3F%20current%20%2B%20%22%5Cn%22%20%2B%20line%20%3A%20line%3B%0A%20%20%20%20%7D%0A%20%20%20%20hasLogs%20%3D%20true%3B%0A%20%20%20%20panel.scrollTop%20%3D%20panel.scrollHeight%3B%0A%20%20%7D%3B%0A%0A%20%20var%20methods%20%3D%20%5B%22log%22%2C%20%22info%22%2C%20%22warn%22%2C%20%22error%22%5D%3B%0A%20%20var%20proxyConsole%20%3D%20Object.create(console)%3B%0A%20%20for%20(var%20i%20%3D%200%3B%20i%20%3C%20methods.length%3B%20i%20%2B%3D%201)%20%7B%0A%20%20%20%20(function%20(methodName)%20%7B%0A%20%20%20%20%20%20var%20fallback%20%3D%20typeof%20console.log%20%3D%3D%3D%20%22function%22%20%3F%20console.log.bind(console)%20%3A%20function%20()%20%7B%7D%3B%0A%20%20%20%20%20%20var%20original%20%3D%0A%20%20%20%20%20%20%20%20typeof%20console%5BmethodName%5D%20%3D%3D%3D%20%22function%22%0A%20%20%20%20%20%20%20%20%20%20%3F%20console%5BmethodName%5D.bind(console)%0A%20%20%20%20%20%20%20%20%20%20%3A%20fallback%3B%0A%20%20%20%20%20%20proxyConsole%5BmethodName%5D%20%3D%20function%20()%20%7B%0A%20%20%20%20%20%20%20%20var%20args%20%3D%20Array.prototype.slice.call(arguments)%3B%0A%20%20%20%20%20%20%20%20original.apply(console%2C%20args)%3B%0A%20%20%20%20%20%20%20%20append(methodName%2C%20args)%3B%0A%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%7D)(methods%5Bi%5D)%3B%0A%20%20%7D%0A%0A%20%20try%20%7B%0A%20%20%20%20var%20run%20%3D%20new%20Function(%22console%22%2C%20%22root%22%2C%20%22panel%22%2C%20%22const%20hoverCard%20%3D%20document.getElementById('my-hover-card')%3B%5Cn%5Cn%20%20hoverCard.addEventListener('hover-card%3Achange'%2C%20(e)%20%3D%3E%20%7B%5Cn%20%20%20%20console.log('Is%20open%3A'%2C%20e.detail.open)%3B%5Cn%20%20%20%20console.log('Reason%3A'%2C%20e.detail.reason)%3B%5Cn%20%20%7D)%3B%22)%3B%0A%20%20%20%20run(proxyConsole%2C%20root%2C%20panel)%3B%0A%20%20%7D%20catch%20(error)%20%7B%0A%20%20%20%20var%20fallbackError%20%3D%20typeof%20console.error%20%3D%3D%3D%20%22function%22%20%3F%20console.error.bind(console)%20%3A%20function%20()%20%7B%7D%3B%0A%20%20%20%20fallbackError(error)%3B%0A%20%20%20%20append(%22error%22%2C%20%5Berror%5D)%3B%0A%20%20%7D%0A%7D)()%3B"></script>
</div>

```astro nocollapse console
<HoverCard id="my-hover-card">
  <HoverCardTrigger asChild>
    <Button variant="outline">Hover me</Button>
  </HoverCardTrigger>
  <HoverCardContent>
    Hover card content
  </HoverCardContent>
</HoverCard>

<script>
  const hoverCard = document.getElementById('my-hover-card');

  hoverCard.addEventListener('hover-card:change', (e) => {
    console.log('Is open:', e.detail.open);
    console.log('Reason:', e.detail.reason);
  });
</script>
```

### Programmatic Control

You can control the hover card programmatically by dispatching a `hover-card:set` event:

```js nocollapse
const hoverCard = document.getElementById('my-hover-card');

// Open the hover card
hoverCard.dispatchEvent(new CustomEvent('hover-card:set', {
  detail: { open: true }
}));

// Close the hover card
hoverCard.dispatchEvent(new CustomEvent('hover-card:set', {
  detail: { open: false }
}));
```

### Data Attributes

The hover card sets these data attributes that you can use for styling or querying state:

| Attribute | Element | Description |
|-----------|---------|-------------|
| `data-state` | hover-card, hover-card-content | Current state (`open` or `closed`) |
| `data-open` | hover-card, hover-card-content | Present while open |
| `data-closed` | hover-card, hover-card-content | Present while closed |
| `data-side` | hover-card-content | Position relative to trigger (`top`, `right`, `bottom`, or `left`) |
| `data-align` | hover-card-content | Alignment (`start`, `center`, or `end`) |
| `data-side-offset` | hover-card-content | Distance from trigger in pixels (default `4`) |
| `data-align-offset` | hover-card-content | Alignment-axis offset in pixels (default `0`) |