Popover
Displays rich content in a portal triggered by a button or any custom element.
Import
import { AvPopoverImports } from '@avesra/angular';Usage
import { Component } from '@angular/core';
import {
AvButtonComponent,
AvPopoverImports,
} from '@avesra/angular';
@Component({
selector: 'app-popover-basic-demo',
imports: [
AvButtonComponent,
AvPopoverImports,
],
template: `<div class="flex items-center gap-4">
<av-popover>
<button av-button av-popover-trigger>Click me</button>
<av-popover-content>
<div av-popover-dialog class="max-w-64">
<h2 av-popover-heading>Popover Title</h2>
<p class="mt-2 text-sm text-muted">
This is the popover content. You can put any content here.
</p>
</div>
</av-popover-content>
</av-popover>
</div>`,
})
export class PopoverBasicDemo {}Anatomy
Import the Popover parts and compose them with element and attribute selectors. Optional pieces include av-popover-arrow and av-popover-heading.
<av-popover>
<button av-button av-popover-trigger>Open</button>
<av-popover-content>
<av-popover-arrow />
<div av-popover-dialog>
<h2 av-popover-heading>Heading</h2>
<!-- content goes here -->
</div>
</av-popover-content>
</av-popover>With Arrow
Add av-popover-arrow inside av-popover-content to point at the trigger. Arrow rotation follows [data-placement] on the overlay panel.
import { Component } from '@angular/core';
import {
AvButtonComponent,
AvPopoverImports,
} from '@avesra/angular';
import { AppIconComponent } from '../../components/app-icon/app-icon.component';
@Component({
selector: 'app-popover-with-arrow-demo',
imports: [
AppIconComponent,
AvButtonComponent,
AvPopoverImports,
],
template: `<div class="flex items-center gap-4">
<av-popover>
<button av-button variant="secondary" av-popover-trigger>With Arrow</button>
<av-popover-content>
<av-popover-arrow />
<div av-popover-dialog class="max-w-64">
<h2 av-popover-heading>Popover with Arrow</h2>
<p class="mt-2 text-sm text-muted">
The arrow shows which element triggered the popover.
</p>
</div>
</av-popover-content>
</av-popover>
<av-popover>
<button
av-button
icon-only
variant="tertiary"
av-popover-trigger
aria-label="More options"
>
<app-icon icon="solar:menu-dots-linear" size="20" />
</button>
<av-popover-content [offset]="10">
<av-popover-arrow />
<div av-popover-dialog class="max-w-64">
<h2 av-popover-heading>Popover with Arrow</h2>
<p class="mt-2 text-sm text-muted">
The arrow shows which element triggered the popover.
</p>
</div>
</av-popover-content>
</av-popover>
</div>`,
})
export class PopoverWithArrowDemo {}Interactive Content
Popover panels accept focusable content — buttons, links, and form controls — while the overlay stays open. Clicking inside the panel does not dismiss the popover.
Maya Ellison
@mayae
import { Component, signal } from '@angular/core';
import {
AvAvatarImports,
AvButtonComponent,
AvPopoverImports,
} from '@avesra/angular';
@Component({
selector: 'app-popover-interactive-demo',
imports: [
AvAvatarImports,
AvButtonComponent,
AvPopoverImports,
],
template: `<div class="flex items-center gap-6">
<av-popover>
<div av-popover-trigger class="flex items-center gap-2" aria-label="User profile">
<span av-avatar size="sm">
<img av-avatar-image alt="Maya Ellison" src="/images/avatars/avatar-woman-glasses-auburn-hair.png" />
<span av-avatar-fallback>ME</span>
</span>
<div class="flex flex-col">
<p class="text-sm font-medium text-foreground">Maya Ellison</p>
<p class="text-xs text-muted">@mayae</p>
</div>
</div>
<av-popover-content>
<div av-popover-dialog class="w-[320px]">
<div av-popover-heading>
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<span av-avatar size="md">
<img av-avatar-image alt="Maya Ellison" src="/images/avatars/avatar-woman-glasses-auburn-hair.png" />
<span av-avatar-fallback>ME</span>
</span>
<div>
<p class="font-semibold text-foreground">Maya Ellison</p>
<p class="text-sm text-muted">@mayae</p>
</div>
</div>
<button
av-button
size="sm"
class="rounded-full"
[variant]="following() ? 'tertiary' : 'primary'"
(click)="toggleFollowing()"
>
{{ following() ? 'Following' : 'Follow' }}
</button>
</div>
</div>
<p class="mt-3 text-sm text-muted">
Product designer based in Lisbon. Shipping interfaces that feel calm and considered.
</p>
<div class="mt-3 flex gap-4">
<div>
<span class="font-semibold text-foreground">428</span>
<span class="ml-1 text-sm text-muted">Following</span>
</div>
<div>
<span class="font-semibold text-foreground">8.2K</span>
<span class="ml-1 text-sm text-muted">Followers</span>
</div>
</div>
</div>
</av-popover-content>
</av-popover>
</div>`,
})
export class PopoverInteractiveDemo {
readonly following = signal(false);
toggleFollowing(): void {
this.following.update((value) => !value);
}
}Placement
Set placement on av-popover-content — top, bottom, left, right, and aligned variants such as bottom start. The overlay flips when should-flip is enabled (default).
import { Component } from '@angular/core';
import {
AvButtonComponent,
AvPopoverImports,
} from '@avesra/angular';
@Component({
selector: 'app-popover-placements-demo',
imports: [
AvButtonComponent,
AvPopoverImports,
],
template: `<div class="grid grid-cols-3 gap-4">
<div></div>
<av-popover>
<button av-button class="w-full" variant="tertiary" av-popover-trigger>Top</button>
<av-popover-content placement="top">
<av-popover-arrow />
<div av-popover-dialog>
<p class="text-sm">Top placement</p>
</div>
</av-popover-content>
</av-popover>
<div></div>
<av-popover>
<button av-button class="w-full" variant="tertiary" av-popover-trigger>Left</button>
<av-popover-content placement="left">
<av-popover-arrow />
<div av-popover-dialog>
<p class="text-sm">Left placement</p>
</div>
</av-popover-content>
</av-popover>
<div class="flex items-center justify-center">
<span class="text-sm text-muted">Click buttons</span>
</div>
<av-popover>
<button av-button class="w-full" variant="tertiary" av-popover-trigger>Right</button>
<av-popover-content placement="right">
<av-popover-arrow />
<div av-popover-dialog>
<p class="text-sm">Right placement</p>
</div>
</av-popover-content>
</av-popover>
<div></div>
<av-popover>
<button av-button class="w-full" variant="tertiary" av-popover-trigger>Bottom</button>
<av-popover-content placement="bottom">
<av-popover-arrow />
<div av-popover-dialog>
<p class="text-sm">Bottom placement</p>
</div>
</av-popover-content>
</av-popover>
<div></div>
</div>`,
})
export class PopoverPlacementsDemo {}Styling
Passing Tailwind CSS classes
Style the portaled panel with overlay-class on av-popover-content, or pass utility classes on div[av-popover-dialog] and other part hosts.
import { Component } from '@angular/core';
import {
AvButtonComponent,
AvPopoverImports,
} from '@avesra/angular';
@Component({
selector: 'app-popover-custom-styling-demo',
imports: [
AvButtonComponent,
AvPopoverImports,
],
template: `<div class="flex items-center gap-4">
<av-popover>
<button av-button av-popover-trigger>Open</button>
<av-popover-content overlay-class="bg-accent text-accent-foreground">
<div av-popover-dialog class="max-w-64">
<h2 av-popover-heading>Custom Styled</h2>
<p class="mt-2 text-sm opacity-90">This popover has custom styling</p>
</div>
</av-popover-content>
</av-popover>
</div>`,
})
export class PopoverCustomStylingDemo {}Customizing the component classes
To customize the Popover classes, use the @layer components directive. Learn more.
@layer components {
.av-popover {
@apply rounded-xl shadow-2xl;
}
.av-popover__dialog {
@apply p-4;
}
.av-popover__heading {
@apply text-lg font-bold;
}
}Avesra follows the BEM methodology so component variants and states stay reusable and easy to customize.
CSS Classes
The Popover component uses these CSS classes:
Base Classes
.av-popover— Base popover container styles.av-popover__dialog— Dialog content wrapper.av-popover__heading— Heading text styles.av-popover__trigger— Trigger element styles[data-slot="popover-arrow"]— Arrow positioning wrapper[data-slot="popover-overlay-arrow"]— SVG fill matched to overlay background
Interactive States
The component supports these animation and interaction states:
- Entering:
[data-entering]— Applied during popover appearance - Exiting:
[data-exiting]— Applied during popover disappearance - Placement:
[data-placement="*"]— Applied based on popover position - Focus:
:focus-visibleor[data-focus-visible="true"] - Expanded:
[aria-expanded]on the trigger reflects open state
API Reference
Compound API across av-popover, [av-popover-trigger], av-popover-content, and child parts. Root props control open state and dismiss behavior; content props control placement and viewport flipping.
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Controls whether the popover is open. Supports two-way binding with [(open)] (av-popover). |
dismissable | boolean | true | Closes the popover when clicking outside (av-popover). |
keyboard-dismiss-disabled | boolean | false | Disables closing via the Escape key (av-popover). |
aria-label | string | — | Accessible label when the trigger has no visible text ([av-popover-trigger]). |
disabled | boolean | false | Disables the trigger ([av-popover-trigger]). |
placement | 'top' | 'top start' | 'top end' | 'bottom' | 'bottom start' | 'bottom end' | 'left' | 'left top' | 'left bottom' | 'right' | 'right top' | 'right bottom' | 'start' | 'start top' | 'start bottom' | 'end' | 'end top' | 'end bottom' | 'bottom' | Preferred placement relative to the trigger (av-popover-content). |
offset | number | 8 | Distance between trigger and popover in pixels (av-popover-content). |
should-flip | boolean | true | Whether the popover can flip to fit the viewport (av-popover-content). |
overlay-class | string | — | Extra classes applied to the portaled overlay panel (av-popover-content). |
overlay-style | Record<string, string | number> | — | Inline styles applied to the portaled overlay panel (av-popover-content). |