Skip to main content

Popover

The Popover component is a fully headless and accessible UI primitive for displaying floating content. It's inspired by HeadlessUI Popover, but built natively for Angular.

It is useful for building tooltips, dropdown menus, context menus, and other floating UI elements — while giving you complete control over markup and styling.

When to use it

Use Popover when you need a floating content component that is:

  • Fully customizable in layout and appearance
  • Accessible with screen readers and keyboard navigation
  • Positioned relative to a trigger element
  • Dismissible via click outside or escape key
  • Compatible with complex content layouts

Common use cases:

  • Dropdown menus
  • Tooltips with rich content
  • Context menus
  • Help popovers
  • Navigation menus

Anatomy

A complete Popover is composed of three components:

  • Popover - The provider and wrapper. Manages internal open/close state.
  • PopoverButton - The trigger that toggles the popover open or closed.
  • PopoverPanel - The floating content container.

All three components are standalone and composable.

Features

  • ✅ Accessible by default (ARIA attributes)
  • ✅ Tailwind-friendly (no styles imposed)
  • ✅ Multiple selectors supported:
    • <Popover>
    • <div ngxPopover>
    • <ngx-headlessui-popover>
  • ✅ Works with both #templateRefs and Angular DI (PopoverContextService)
  • ✅ Supports multiple instances per page
  • ✅ Click outside to close
  • ✅ Escape key to close

Installation

Popover ships as part of the @ngx-headless/ui by default. Install if you haven't already.

npm install @ngx-headless/ui

Import the components directly:

import {
PopoverComponent,
PopoverButtonComponent,
PopoverPanelComponent,
} from "@ngx-headless/ui";

Usage Examples

Template Reference — Basic Popover

<Popover #p="ngxPopover">
<PopoverButton>Open Popover</PopoverButton>
<PopoverPanel *ngIf="p.isOpen()">
<div class="popover-content">
<h3>Popover Title</h3>
<p>This is the popover content.</p>
</div>
</PopoverPanel>
</Popover>

Template Reference — Rich Content Popover

<Popover #p="ngxPopover" class="relative">
<PopoverButton class="btn">
More Options
</PopoverButton>
<PopoverPanel *ngIf="p.isOpen()" class="absolute top-full left-0 mt-2 w-64">
<div class="bg-white rounded-lg shadow-lg p-4">
<div *ngFor="let option of options" class="py-2">
<a [href]="option.href" class="block hover:bg-gray-100 p-2 rounded">
{{ option.label }}
</a>
</div>
</div>
</PopoverPanel>
</Popover>

Accessibility

This component handles

  • aria-expanded on the PopoverButton
  • role="button" on the PopoverButton
  • Keyboard interaction: toggle on Enter / Space, close on Escape
  • Click outside to close behavior
  • Screen reader compatibility for open/close state

Animations

PopoverPanel can be animated freely using Angular's built-in animation system.

Example

Add an animation trigger to your component:

import { trigger, transition, style, animate } from "@angular/animations";

@Component({
animations: [
trigger("fadeIn", [
transition(":enter", [
style({ opacity: 0, transform: "scale(0.95)" }),
animate("100ms ease-out", style({ opacity: 1, transform: "scale(1)" })),
]),
transition(":leave", [
animate("75ms ease-in", style({ opacity: 0, transform: "scale(0.95)" })),
]),
]),
],
})
export class ExampleComponent {}

Use it in the template with *ngIf:

<PopoverPanel *ngIf="popover.isOpen()" @fadeIn>
Content goes here.
</PopoverPanel>

Component API

PopoverComponent

InputTypeDescription
classstringClass for styling wrapper
MethodDescription
isOpen()Returns true if popover open
toggle()Toggles popover open/close
open()Opens popover
close()Closes popover

PopoverButtonComponent

  • Automatically binds aria-expanded and role
  • Toggles popover on click and keyboard interaction
  • Handles Enter and Space key activation

PopoverPanelComponent

  • Visible only when Popover is open
  • Can be styled or animated freely
  • hidden attribute used for visibility
  • Supports complex content layouts

Positioning

The PopoverPanel does not include built-in positioning logic. You can position it using CSS classes or a positioning library like Floating UI.

CSS Positioning Example

<Popover class="relative">
<PopoverButton>Trigger</PopoverButton>
<PopoverPanel *ngIf="popover.isOpen()"
class="absolute top-full left-0 mt-2 w-64 z-10">
Content
</PopoverPanel>
</Popover>

With Floating UI

import { computePosition, flip, shift, offset } from '@floating-ui/dom';

// In your component
async updatePosition() {
const { x, y } = await computePosition(this.button, this.panel, {
middleware: [offset(10), flip(), shift({ padding: 5 })],
});

Object.assign(this.panel.style, {
left: `${x}px`,
top: `${y}px`,
});
}

See Also