Skip to main content
WireKit
Copy for LLM

Popover

The <x-wirekit::popover> component creates a click-triggered floating panel anchored to a trigger element. Unlike a Tooltip (hover, plain text) or Hover Card (hover, rich content), the Popover is activated on click and is ideal for small forms, share menus, or quick settings.

Usage

Share Options Popover

Compact URL Copy

For a space-efficient share popover, place the copy icon directly inside the input:

Compact URL Copy

Use padding-right on the input to reserve space so text stops before the icon area. The icon button is hand-rolled for the icon-only embedded case — <x-wirekit::clipboard-button> always reserves layout for its Copied! label (a stable-width feature for labeled use) which would render too wide inside an input. For long URLs, add a gradient fade overlay between the text and the icon button.

Placement

Control where the panel appears relative to the trigger:

Popover Placement Options
<x-wirekit::popover placement="bottom-start">
    <x-slot:trigger>
        <x-wirekit::button>Options</x-wirekit::button>
    </x-slot:trigger>
    Panel content
</x-wirekit::popover>

Available placements: top, top-start, top-end, bottom (default), bottom-start, bottom-end, left, left-start, left-end, right, right-start, right-end.

The panel automatically flips to the opposite side when there is not enough space and shifts to stay within the viewport, powered by Floating UI.

With Form Content

Popovers are well suited for inline forms that don't warrant a full modal:

Inline Rename Form
<x-wirekit::popover placement="bottom-end">
    <x-slot:trigger>
        <x-wirekit::button intent="neutral" surface="outline" size="sm">Rename</x-wirekit::button>
    </x-slot:trigger>
    <form wire:submit="rename" class="flex flex-col gap-3" style="width: 18rem;">
        <x-wirekit::input name="new-name" label="New name" />
        <x-wirekit::button type="submit" size="sm">Save</x-wirekit::button>
    </form>
</x-wirekit::popover>

Popover vs Tooltip vs Hover Card vs Dropdown

Feature Tooltip Hover Card Popover Dropdown
Trigger Hover / Focus Hover / Focus Click Click
Content Plain text Rich HTML (read-only) Interactive (forms, buttons) Menu items / actions
ARIA role tooltip dialog dialog menu
Focus trapped No No Yes No (roving tabindex)
Use case Labels, abbreviations User profiles, previews Inline forms, share menus Action lists

Width & Layout

Popover width is determined by its content. To set an explicit width, apply Tailwind classes to the panel slot content:

<x-wirekit::popover>
    <x-slot:trigger>…</x-slot:trigger>
    <div class="w-72 p-4">Fixed-width popover content.</div>
</x-wirekit::popover>

Behavior

  • Floating UI (bundled ~3.5 KB) handles positioning with flip and shift middleware
  • Focus trap (bundled ~3.8 KB) keeps keyboard navigation within the panel
  • Click outside closes the popover automatically
  • Livewire SPA navigation (wire:navigate) automatically closes open popovers
  • Transitions use scale + opacity for smooth open/close animation

Props

Prop Type Default Description
placement string 'bottom' Floating UI placement
offset int 8 Distance in px between trigger and panel
label string|null null The panel's accessible name. It is a role="dialog", so a screen reader announces this on entry; without it the announcement is the generic word "Popover", which is the same for every popover on the page.
padded bool true Whether the panel pads its own contents. Pass false for a panel that brings its own header, scroll region or footer — those have to reach the panel's edges, and outer padding puts a gutter between the scrollbar and the border.
scope string|null null Scoped personalization key

Slots

Slot Purpose
trigger The element that toggles the popover on click
default Interactive content inside the floating panel

Accessibility

  • Trigger: aria-haspopup="dialog" -- announces that activation opens a dialog-like panel
  • Trigger: aria-expanded -- toggles between "true" and "false"
  • Trigger: aria-controls -- linked to the panel's unique id (auto-generated)
  • Panel: role="dialog" -- screen readers announce content as a dialog
  • Panel: aria-modal="false" -- the popover does not block the rest of the page semantically (unlike a modal)
  • Focus trap active when open -- Tab cycles within the panel only
  • Focus returns to the trigger element on close
  • Click outside closes the popover
  • Escape closes the popover
  • SPA-safe: cleans up on Livewire page navigation (wire:navigate)

Keyboard Interaction

Key Action
Enter / Space (on trigger) Toggle the popover
Tab / Shift+Tab (inside the open popover) Cycle focus through interactive content (focus trap)
Escape Close the popover and return focus to the trigger

Pitfalls

  • Don't use a popover where a tooltip would do. Tooltips describe; popovers contain interactive content. The wrong choice misses focus management.
  • Don't auto-open a popover on page load. It steals focus and confuses screen-reader users — popovers are user-triggered by design.

Design Tokens

Element Token
Panel background --color-wk-bg-elevated
Panel border --color-wk-border / --border-wk-width
Panel radius --radius-wk-lg
Panel shadow --shadow-wk-lg
Panel padding --padding-wk-x-md
Text color --color-wk-text
Text size --text-wk-md
Font family --font-wk-sans
Transition --transition-wk-duration

Personalization

Override defaults in config/wirekit.php:

'components' => [
    'popover' => [
        'placement' => 'bottom-start',
        'offset' => 12,
    ],
],

Scoped Personalization

Apply different styles to specific instances using the scope prop:

<x-wirekit::popover scope="share-menu">
    ...
</x-wirekit::popover>
// config/wirekit.php
'personalizations' => [
    'popover' => [
        'share-menu' => [
            'base' => 'max-w-sm',
        ],
    ],
],

Further Reading

Was this page helpful?

Voting requires cookies or local storage. What we store