Skip to content

Popover <l-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.

HTML tag<l-popover>
Native HTML
Progressive
Custom
Shadow DOM
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.

Jane Cooper

Senior Engineer at Acme Corp

Code
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.

Placed on top Placed on bottom Placed on left Placed on right
Code
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.

This popover has no arrow.

Code
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.

Quick preview

Move your cursor freely between trigger and popover.

Code
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.

Safe area demo

The red polygon keeps the popover open while your cursor travels between trigger and content.

Mega menu

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

Code
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

WCAG4.1.2
RGAA7.1
Dismissible

Closes on Escape and click outside (light-dismiss via popover="auto")

WCAG3.2.2
Hover bridge

Safe polygon prevents flickering when moving cursor from trigger to popover

WCAG1.4.13
Focus state

Visible focus ring on trigger for keyboard users

WCAG2.4.7
RGAA10.7
Motion

Respects prefers-reduced-motion

WCAG2.3.3

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

forAttribute
ID of the trigger element
placementAttribute
Preferred placement: bottom (default), bottom-start, bottom-end, top, top-start, top-end, left, left-start, left-end, right, right-start, right-end
distanceAttribute
Offset from trigger in px. Default 8
openAttribute
Whether popover is visible. Reflects to attribute
without-arrowAttribute
Hide the directional arrow
full-widthAttribute
Stretch the popover to the viewport width. Useful for mega menus — typically combined with without-arrow
triggerAttribute
Space-separated trigger modes: click (default), hover, focus, manual

Methods

show()Method
Shows the popover
hide()Method
Hides the popover
toggle()Method
Toggles the popover

CSS custom properties

--backgroundName
Background color. Default: Canvas
--colorName
Text color. Default: inherited
--border-radiusName
Border radius. Default 8px
--max-widthName
Maximum width. Default 320px
--shadowName
Box shadow
--arrow-sizeName
Arrow size. Default 8px
--show-durationName
Show animation duration. Default 150ms
--hide-durationName
Hide animation duration. Default 150ms