Advanced Components

Popover

A floating panel that appears next to a trigger element. Useful for tooltips with rich content, contextual menus, additional details, or any supplementary information that should appear on demand.

Example

A simple popover with a title and description.

Basic popover
<div buiPopover>
<button matButton buiPopoverTrigger>Open Popover</button>
<ng-template buiPopoverPortal>
<div buiPopoverContent>
<h3 buiPopoverTitle>Dimensions</h3>
<p buiPopoverDescription>
Set the dimensions for the layer. Width and height
can be adjusted independently.
</p>
</div>
</ng-template>
</div>

Structure

The popover is fully directive-based. You compose it by applying attribute directives to standard HTML elements.

<div buiPopover>
<button matButton buiPopoverTrigger>Open</button>

<ng-template buiPopoverPortal>
<div buiPopoverContent>
<h3 buiPopoverTitle>Title</h3>
<p buiPopoverDescription>Description</p>
<button matButton buiPopoverClose>Close</button>
</div>
</ng-template>
</div>

<!-- With an external anchor -->
<span buiPopoverAnchor #myAnchor="buiPopoverAnchor">
Anchor
</span>

<div buiPopover [anchor]="myAnchor">
<button matButton buiPopoverTrigger>Open</button>

<ng-template buiPopoverPortal>
<div buiPopoverContent>
<h3 buiPopoverTitle>Title</h3>
<p buiPopoverDescription>Description</p>
<button matButton buiPopoverClose>Close</button>
</div>
</ng-template>
</div>

The building blocks are:

DirectiveDescription
buiPopover Root container. Manages open state, anchor positioning, and the CDK overlay.
buiPopoverTrigger Opens the popover when clicked. Also serves as the default positioning anchor unless an external anchor is provided.
buiPopoverAnchor Marks an element as the positioning anchor for the popover, allowing the overlay to be positioned relative to a different element than the trigger. Optional.
buiPopoverPortal Wraps the content that is rendered inside a CDK overlay when the popover opens.
buiPopoverContentThe floating panel itself. Handles entry/exit animations.
buiPopoverCloseCloses the popover when clicked.
buiPopoverTitle Title text inside the content. Connects to aria-labelledby. Optional.
buiPopoverDescription Description text inside the content. Connects to aria-describedby. Optional.

buiPopover

The root directive. Apply it to any element to create a popover instance. It manages the open state, positions the overlay relative to the trigger, and handles dismissal via backdrop click or Escape key.

<!-- Default (bottom) -->
<div buiPopover>...</div>

<!-- Custom position -->
<div buiPopover position="top-start">...</div>

<!-- Custom offset -->
<div buiPopover [offset]="8">...</div>

<!-- Two-way open binding -->
<div buiPopover [(open)]="isOpen">...</div>

<!-- Close on scroll -->
<div buiPopover [closeOnScroll]="true">...</div>

<!-- External anchor -->
<div buiPopover [anchor]="myAnchor">...</div>
PropTypeDefaultDescription
openmodel<boolean>falseTwo-way binding for the open state.
anchorBuiPopoverAnchor | ElementRef | Element | { x: number; y: number } | nullnull An external anchor to position the popover against. When set, the overlay is positioned relative to this anchor instead of the trigger. Accepts a buiPopoverAnchor directive reference, an ElementRef, a native DOM element, or a fixed point ({ x, y }).
positionPopoverPosition'bottom' Preferred placement relative to the trigger. Automatically flips to a fallback position if there is not enough space.
offsetnumber4 Distance in pixels between the trigger and the popover panel.
autoFocus'dialog' | 'first-tabbable' | 'first-heading' | string | undefined'first-tabbable' Configures where focus goes when the dialog opens.
closeOnScrollbooleanfalse When true, the popover automatically closes when the user scrolls any scrollable ancestor.
restoreFocusboolean | string | HTMLElementtrue Configures where focus returns when the dialog closes.
(opened)voidEmits when the popover opens.
(closed)void Emits after the popover closes and its exit animation completes.

The position input accepts one of 12 placement values:

  • top, top-start, top-end
  • bottom, bottom-start, bottom-end
  • start, start-top, start-bottom
  • end, end-top, end-bottom

Trigger

Apply buiPopoverTrigger to a button or any clickable element. It toggles the popover on click, automatically sets aria-expanded, aria-controls, and keyboard support (Enter / Space). The trigger element also serves as the default positioning anchor for the overlay unless an external anchor is provided.

<button matButton buiPopoverTrigger>Open Popover</button>

Anchor

By default the popover is positioned relative to the trigger element. When you need the overlay to appear next to a different element, use the anchor input on buiPopover.

The simplest approach is the buiPopoverAnchor directive. Apply it to any element and pass the reference to the anchor input:

<span buiPopoverAnchor #myAnchor="buiPopoverAnchor">
Anchor element
</span>

<div buiPopover [anchor]="myAnchor">
<button matButton buiPopoverTrigger>Open</button>
<ng-template buiPopoverPortal>
<div buiPopoverContent>...</div>
</ng-template>
</div>

You can also pass an ElementRef, a native DOM Element, or a fixed point ({ x: number; y: number }) for absolute positioning:

<!-- Fixed point -->
<div buiPopover [anchor]="{ x: 200, y: 400 }">
<button matButton buiPopoverTrigger>Open</button>
<ng-template buiPopoverPortal>
<div buiPopoverContent>...</div>
</ng-template>
</div>
External anchor
Anchor element
<div buiPopover [anchor]="externalAnchor">
<button matButton buiPopoverTrigger>Open Popover</button>
<ng-template buiPopoverPortal>
<div buiPopoverContent>
<p buiPopoverDescription>
This popover is anchored to the pill
on the right, not the trigger button.
</p>
</div>
</ng-template>
</div>

<span
buiPopoverAnchor
#externalAnchor="buiPopoverAnchor"
class="rounded-full bg-neutral-a3 px-3 py-1 text-xs font-medium"
>
Anchor element
</span>

Portal

The buiPopoverPortal directive goes on an ng-template. Its content is projected into a CDK overlay anchored to the trigger when the popover opens.

<ng-template buiPopoverPortal>
<div buiPopoverContent>
<!-- title, description, or any custom content -->
</div>
</ng-template>

Content

The buiPopoverContent directive marks the floating panel. It handles entry/exit animations and provides the visual styling (border, shadow, background). Place title, description, or any custom content inside it.

<div buiPopoverContent>
<h3 buiPopoverTitle>Title</h3>
<p buiPopoverDescription>Description text</p>
</div>

<!-- Custom content -->
<div buiPopoverContent>
<p>Any content can go here.</p>
<button matButton buiPopoverClose>Close</button>
</div>

Close

Apply buiPopoverClose to any element inside the portal. It closes the popover when clicked. Attach your own (click) handler separately for additional logic.

<button matButton buiPopoverClose (click)="onSave()">
Save & Close
</button>

Title & Description

buiPopoverTitle and buiPopoverDescription are optional directives for structured content. The title connects to aria-labelledby and the description connects to aria-describedby on the popover container.

<div buiPopoverContent>
<h3 buiPopoverTitle>Dimensions</h3>
<p buiPopoverDescription>
Set the dimensions for the layer.
</p>
</div>

Position

Use the position input to control where the popover appears relative to the trigger. The popover automatically flips to a fallback position if there is not enough space.

Open from different positions
<!-- Bottom (default) -->
<div buiPopover position="bottom">...</div>

<!-- Top -->
<div buiPopover position="top">...</div>

<!-- Start (left in LTR) -->
<div buiPopover position="start">...</div>

<!-- End (right in LTR) -->
<div buiPopover position="end">...</div>

<!-- Aligned variants -->
<div buiPopover position="bottom-start">...</div>
<div buiPopover position="top-end">...</div>

Programmatic control

You can control the popover without a trigger. Use the open model for two-way binding, or grab a template reference via #ref="buiPopover" and call open(), close(), or toggle().

<!-- Two-way binding -->
<div buiPopover [(open)]="isPopoverOpen">
<button matButton buiPopoverTrigger>Open</button>
<ng-template buiPopoverPortal>
<div buiPopoverContent>...</div>
</ng-template>
</div>

<!-- Template reference -->
<div buiPopover #myPopover="buiPopover">
<button matButton buiPopoverTrigger>Open</button>
<ng-template buiPopoverPortal>
<div buiPopoverContent>...</div>
</ng-template>
</div>

<button matButton (click)="myPopover.open()">Open</button>
<button matButton (click)="myPopover.close()">Close</button>
<button matButton (click)="myPopover.toggle()">Toggle</button>