---
outline: deep
---

# Popover

Popovers are used to display rich interactive content in a floating panel anchored to a trigger element. Commonly used for mini forms, additional details, and contextual controls. Dismissed by clicking outside.

**`<l-popover>`** — Custom Element · Shadow DOM

## Options

### Basic

Reference a trigger element by ID using the `for` attribute. Clicks toggle the popover; clicking outside closes it. The popover does not apply any padding — wrap the slotted content in a container with the spacing you want.

```html
<button
  id="popover-basic"
  class="l-button"
>
  View profile
</button>
<l-popover for="popover-basic">
  <div class="flex items-center gap-3 px-4 py-3">
    <l-avatar
      src="https://i.pravatar.cc/150?img=58"
      name="Jane Cooper"
    ></l-avatar>
    <div class="flex flex-col gap-0.5">
      <p class="font-medium text-primary">Jane Cooper</p>
      <p class="text-xs text-secondary">Senior Engineer at Acme Corp</p>
    </div>
    <button
      class="l-button ml-auto"
      data-size="sm"
      data-variant="primary"
    >
      Follow
    </button>
  </div>
</l-popover>
```

### Placement

Set `placement` to control position: `bottom` (default), `top`, `left`, `right`.

```html
<div class="flex flex-wrap items-center gap-4">
  <button
    id="popover-top"
    class="l-button"
  >
    Top
  </button>
  <l-popover
    for="popover-top"
    placement="top"
  >
    <span class="block px-4 py-3">Placed on top</span>
  </l-popover>

  <button
    id="popover-bottom"
    class="l-button"
  >
    Bottom
  </button>
  <l-popover
    for="popover-bottom"
    placement="bottom"
  >
    <span class="block px-4 py-3">Placed on bottom</span>
  </l-popover>

  <button
    id="popover-left"
    class="l-button"
  >
    Left
  </button>
  <l-popover
    for="popover-left"
    placement="left"
  >
    <span class="block px-4 py-3">Placed on left</span>
  </l-popover>

  <button
    id="popover-right"
    class="l-button"
  >
    Right
  </button>
  <l-popover
    for="popover-right"
    placement="right"
  >
    <span class="block px-4 py-3">Placed on right</span>
  </l-popover>
</div>
```

### Without arrow

Add `without-arrow` to hide the directional arrow.

```html
<button
  id="popover-no-arrow"
  class="l-button"
>
  Without arrow
</button>
<l-popover
  for="popover-no-arrow"
  without-arrow
>
  <p class="px-4 py-3">This popover has no arrow.</p>
</l-popover>
```

### Hover trigger

Set `trigger="hover"` to open on pointer enter. A safe polygon hover bridge prevents flickering when moving between trigger and popover.

```html
<button
  id="popover-hover"
  class="l-button"
>
  Hover me
</button>
<l-popover
  for="popover-hover"
  trigger="hover"
>
  <div class="flex flex-col gap-2 px-4 py-3">
    <p class="font-medium">Quick preview</p>
    <p class="text-sm text-gray-500">Move your cursor freely between trigger and popover.</p>
  </div>
</l-popover>
```

## Examples

### Safe triangle visualization

Hover the button and move your cursor toward the popover. The safe polygon keeps the popover open while your cursor travels across the gap.

### Mega menu

E-commerce style mega menu: `full-width` stretches the popover to the viewport, `--show-duration: 0ms` makes it appear instantly on hover.

```html
<style>
  #demo-mega l-popover::part(body) {
    border: 0;
    border-bottom: 1px solid var(--l-color-divider);
  }
</style>

<nav
  id="demo-mega"
  class="vp-raw -mx-3 -my-6 bg-[var(--vp-c-bg)] border-b border-[var(--l-color-divider)]"
>
  <div class="flex items-center px-4">
    <button
      id="mega-women"
      class="cursor-pointer px-5 py-4 text-sm font-medium hover:underline underline-offset-[6px]"
    >
      Femme
    </button>
    <button
      id="mega-men"
      class="cursor-pointer px-5 py-4 text-sm font-medium hover:underline underline-offset-[6px]"
    >
      Homme
    </button>
    <button
      id="mega-kids"
      class="cursor-pointer px-5 py-4 text-sm font-medium hover:underline underline-offset-[6px]"
    >
      Enfant
    </button>
  </div>

  <l-popover
    for="mega-women"
    trigger="hover"
    placement="bottom-start"
    distance="1"
    full-width
    without-arrow
    style="--shadow: none; --border-radius: 0; --show-duration: 0ms; --hide-duration: 0ms"
  >
    <div class="max-w-5xl mx-auto grid grid-cols-4 gap-8 px-6 py-6">
      <div>
        <p class="font-medium mb-3">Vêtements</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Nouveautés</button>
          <button class="cursor-pointer hover:underline">Robes</button>
          <button class="cursor-pointer hover:underline">Tops & T-shirts</button>
          <button class="cursor-pointer hover:underline">Mailles</button>
          <button class="cursor-pointer hover:underline">Manteaux & vestes</button>
        </div>
      </div>
      <div>
        <p class="font-medium mb-3">Chaussures</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Sneakers</button>
          <button class="cursor-pointer hover:underline">Bottes</button>
          <button class="cursor-pointer hover:underline">Escarpins</button>
          <button class="cursor-pointer hover:underline">Ballerines</button>
          <button class="cursor-pointer hover:underline">Sandales</button>
        </div>
      </div>
      <div>
        <p class="font-medium mb-3">Accessoires</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Sacs</button>
          <button class="cursor-pointer hover:underline">Bijoux</button>
          <button class="cursor-pointer hover:underline">Ceintures</button>
          <button class="cursor-pointer hover:underline">Lunettes</button>
          <button class="cursor-pointer hover:underline">Foulards</button>
        </div>
      </div>
      <div class="rounded-lg bg-gray-100 p-4 cursor-pointer hover:bg-gray-200">
        <span class="block text-xs uppercase tracking-wide text-gray-500 mb-1">À la une</span>
        <span class="block font-medium mb-1">Collection printemps 2026</span>
        <span class="block text-sm text-gray-600">Jusqu'à -30 % cette semaine.</span>
      </div>
    </div>
  </l-popover>

  <l-popover
    for="mega-men"
    trigger="hover"
    placement="bottom-start"
    distance="1"
    full-width
    without-arrow
    style="--shadow: none; --border-radius: 0; --show-duration: 0ms; --hide-duration: 0ms"
  >
    <div class="max-w-5xl mx-auto grid grid-cols-4 gap-8 px-6 py-6">
      <div>
        <p class="font-medium mb-3">Vêtements</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Nouveautés</button>
          <button class="cursor-pointer hover:underline">Chemises</button>
          <button class="cursor-pointer hover:underline">T-shirts & polos</button>
          <button class="cursor-pointer hover:underline">Jeans</button>
          <button class="cursor-pointer hover:underline">Vestes & manteaux</button>
        </div>
      </div>
      <div>
        <p class="font-medium mb-3">Chaussures</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Sneakers</button>
          <button class="cursor-pointer hover:underline">Bottes</button>
          <button class="cursor-pointer hover:underline">Mocassins</button>
          <button class="cursor-pointer hover:underline">Sandales</button>
          <button class="cursor-pointer hover:underline">Ville</button>
        </div>
      </div>
      <div>
        <p class="font-medium mb-3">Accessoires</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Montres</button>
          <button class="cursor-pointer hover:underline">Portefeuilles</button>
          <button class="cursor-pointer hover:underline">Ceintures</button>
          <button class="cursor-pointer hover:underline">Lunettes</button>
          <button class="cursor-pointer hover:underline">Sacs</button>
        </div>
      </div>
      <div class="rounded-lg bg-gray-100 p-4 cursor-pointer hover:bg-gray-200">
        <span class="block text-xs uppercase tracking-wide text-gray-500 mb-1">À la une</span>
        <span class="block font-medium mb-1">Workwear essentiel</span>
        <span class="block text-sm text-gray-600">Les basiques pour la nouvelle saison.</span>
      </div>
    </div>
  </l-popover>

  <l-popover
    for="mega-kids"
    trigger="hover"
    placement="bottom-start"
    distance="1"
    full-width
    without-arrow
    style="--shadow: none; --border-radius: 0; --show-duration: 0ms; --hide-duration: 0ms"
  >
    <div class="max-w-5xl mx-auto grid grid-cols-4 gap-8 px-6 py-6">
      <div>
        <p class="font-medium mb-3">Fille</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Nouveautés</button>
          <button class="cursor-pointer hover:underline">Robes</button>
          <button class="cursor-pointer hover:underline">Hauts</button>
          <button class="cursor-pointer hover:underline">Bas</button>
          <button class="cursor-pointer hover:underline">Pyjamas</button>
        </div>
      </div>
      <div>
        <p class="font-medium mb-3">Garçon</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Nouveautés</button>
          <button class="cursor-pointer hover:underline">T-shirts</button>
          <button class="cursor-pointer hover:underline">Pantalons</button>
          <button class="cursor-pointer hover:underline">Sweats</button>
          <button class="cursor-pointer hover:underline">Pyjamas</button>
        </div>
      </div>
      <div>
        <p class="font-medium mb-3">Bébé</p>
        <div class="flex flex-col items-start gap-1.5 text-sm text-gray-600">
          <button class="cursor-pointer hover:underline">Naissance</button>
          <button class="cursor-pointer hover:underline">Bodies</button>
          <button class="cursor-pointer hover:underline">Pyjamas</button>
          <button class="cursor-pointer hover:underline">Tenues de sortie</button>
          <button class="cursor-pointer hover:underline">Accessoires</button>
        </div>
      </div>
      <div class="rounded-lg bg-gray-100 p-4 cursor-pointer hover:bg-gray-200">
        <span class="block text-xs uppercase tracking-wide text-gray-500 mb-1">À la une</span>
        <span class="block font-medium mb-1">Retour en classe</span>
        <span class="block text-sm text-gray-600">Tout pour la rentrée 2026.</span>
      </div>
    </div>
  </l-popover>
</nav>
```

## Accessibility

### Criteria

- **Expanded state** — Trigger receives `aria-expanded` and `aria-controls` pointing to the popover
- **Dismissible** — Closes on `Escape` and click outside (light-dismiss via `popover="auto"`)
- **Hover bridge** — Safe polygon prevents flickering when moving cursor from trigger to popover
- **Focus state** — Visible focus ring on trigger for keyboard users
- **Motion** — Respects `prefers-reduced-motion`

### Rules

- Use a `<button>` element as the trigger — the component links it via `aria-controls` automatically
- If the popover contains interactive content, ensure a logical focus order inside it
- Add an accessible label to the popover content when it functions as a distinct region

### Keyboard interactions

- `Enter` — Toggles the popover (click trigger)
- `Space` — Toggles the popover (click trigger)
- `Escape` — Closes the popover
- `Tab` — Moves focus through focusable elements inside the popover, then out

## API reference

### Importing

```js
import 'luxen-ui/popover';
```

### Attributes & Properties

- **for**: `string` — The HTML id of the element triggering the popover.
- **placement**: `Placement` (default: `'bottom'`) — The preferred placement of the popover.
- **distance**: `number` (default: `8`) — The distance in pixels from the target element.
- **open**: `boolean` (default: `false`) — Whether or not the popover is visible.
- **without-arrow**: `boolean` (default: `false`) — Hide the directional arrow.
- **full-width**: `boolean` (default: `false`) — Stretch the popover to the viewport width. Useful for mega menus.
- **trigger**: `string` (default: `'click'`) — Space-separated list of trigger modes: `click`, `hover`, `focus`, `manual`.

### Methods

- **show()**
- **hide()**
- **toggle()**

### CSS custom properties

- `--background` — Background color. Default: `Canvas`.
- `--color` — Text color. Default: inherited.
- `--border-radius` (default: `8px`) — Border radius.
- `--max-width` (default: `320px`) — Maximum width.
- `--shadow` — Box shadow.
- `--arrow-size` (default: `8px`) — Arrow size.
- `--show-duration` (default: `150ms`) — Show animation duration.
- `--hide-duration` (default: `150ms`) — Hide animation duration.
