popover

Displays rich content in a portal, triggered by a button.

PreviousNext
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardInputComponent } from '@/shared/components/input/input.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';

@Component({
  selector: 'z-demo-popover-preview',
  imports: [ZardButtonComponent, ZardInputComponent, ...ZardPopoverImports],
  template: `
    <button type="button" z-button zPopover zType="outline" [zContent]="popoverContent">Open popover</button>

    <ng-template #popoverContent>
      <z-popover class="w-80">
        <div z-popover-header>
          <h4 z-popover-title>Dimensions</h4>
          <p z-popover-description>Set the dimensions for the layer.</p>
        </div>

        <div class="grid gap-2">
          @for (dimension of dimensions; track dimension.id) {
            <div class="grid grid-cols-3 items-center gap-4">
              <label class="text-sm" [attr.for]="dimension.id">{{ dimension.label }}</label>
              <input
                z-input
                type="text"
                class="col-span-2 h-8"
                [id]="dimension.id"
                [value]="dimension.value"
                [attr.aria-label]="dimension.label"
              />
            </div>
          }
        </div>
      </z-popover>
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverPreviewComponent {
  readonly dimensions = [
    { id: 'width', label: 'Width', value: '100%' },
    { id: 'maxWidth', label: 'Max. width', value: '300px' },
    { id: 'height', label: 'Height', value: '25px' },
    { id: 'maxHeight', label: 'Max. height', value: 'none' },
  ];
}

Installation

Copy
npx zard-cli@latest add popover

Usage

import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';
Copy
<button z-button zType="outline" zPopover [zContent]="popoverContent">Open popover</button>
<ng-template #popoverContent>
  <z-popover>
    <div z-popover-header>
      <h4 z-popover-title>Title</h4>
      <p z-popover-description>Description text here.</p>
    </div>
  </z-popover>
</ng-template>
Copy

Composition

Use the following composition to build a popover:

button[zPopover]
└── ng-template
    └── z-popover
        └── div[z-popover-header]
            ├── h4[z-popover-title]
            └── p[z-popover-description]
Copy

Examples

basic

A popover with a header, a title and a description, aligned to the start of the trigger.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';

@Component({
  selector: 'z-demo-popover-basic',
  imports: [ZardButtonComponent, ...ZardPopoverImports],
  template: `
    <button type="button" z-button zPopover zAlign="start" zType="outline" class="w-fit" [zContent]="popoverContent">
      Open Popover
    </button>

    <ng-template #popoverContent>
      <z-popover>
        <div z-popover-header>
          <h4 z-popover-title>Dimensions</h4>
          <p z-popover-description>Set the dimensions for the layer.</p>
        </div>
      </z-popover>
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverBasicComponent {}

align

Use zAlign to align the popover against the trigger.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';

@Component({
  selector: 'z-demo-popover-align',
  imports: [ZardButtonComponent, ...ZardPopoverImports],
  template: `
    <div class="flex gap-6">
      @for (alignment of alignments; track alignment.align) {
        <button
          type="button"
          z-button
          zPopover
          zSize="sm"
          zType="outline"
          [zAlign]="alignment.align"
          [zContent]="popoverContent"
        >
          {{ alignment.label }}
        </button>

        <ng-template #popoverContent>
          <z-popover class="w-40">{{ alignment.content }}</z-popover>
        </ng-template>
      }
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverAlignComponent {
  readonly alignments = [
    { align: 'start', label: 'Start', content: 'Aligned to start' },
    { align: 'center', label: 'Center', content: 'Aligned to center' },
    { align: 'end', label: 'End', content: 'Aligned to end' },
  ] as const;
}

form

A popover holding a form built with the z-field components.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardInputComponent } from '@/shared/components/input/input.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';

@Component({
  selector: 'z-demo-popover-form',
  imports: [ZardButtonComponent, ZardInputComponent, ...ZardFieldImports, ...ZardPopoverImports],
  template: `
    <button type="button" z-button zPopover zAlign="start" zType="outline" [zContent]="popoverContent">
      Open Popover
    </button>

    <ng-template #popoverContent>
      <z-popover class="w-64">
        <div z-popover-header>
          <h4 z-popover-title>Dimensions</h4>
          <p z-popover-description>Set the dimensions for the layer.</p>
        </div>

        <div z-field-group class="gap-4">
          <div z-field zOrientation="horizontal">
            <label z-field-label for="form-width" class="w-1/2">Width</label>
            <input z-input type="text" id="form-width" value="100%" />
          </div>

          <div z-field zOrientation="horizontal">
            <label z-field-label for="form-height" class="w-1/2">Height</label>
            <input z-input type="text" id="form-height" value="25px" />
          </div>
        </div>
      </z-popover>
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverFormComponent {}

placement

Use zPlacement to choose the side the popover opens on.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';

@Component({
  selector: 'z-demo-popover-placement',
  imports: [ZardButtonComponent, ...ZardPopoverImports],
  template: `
    <div class="flex flex-col space-y-2">
      <button type="button" z-button zPopover zPlacement="top" zType="outline" [zContent]="popoverContent">Top</button>

      <div class="flex space-x-2">
        <button type="button" z-button zPopover zPlacement="left" zType="outline" [zContent]="popoverContent">
          Left
        </button>
        <button type="button" z-button zPopover zPlacement="right" zType="outline" [zContent]="popoverContent">
          Right
        </button>
      </div>

      <button type="button" z-button zPopover zPlacement="bottom" zType="outline" [zContent]="popoverContent">
        Bottom
      </button>
    </div>

    <ng-template #popoverContent>
      <z-popover class="w-64">
        <div z-popover-header>
          <h4 z-popover-title>Placement</h4>
          <p z-popover-description>The popover flips automatically when it does not fit.</p>
        </div>
      </z-popover>
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverPlacementComponent {}

hover

Set zTrigger="hover" to open the popover on pointer enter.
import { ChangeDetectionStrategy, Component } from '@angular/core';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';

@Component({
  selector: 'z-demo-popover-hover',
  imports: [ZardButtonComponent, ...ZardPopoverImports],
  template: `
    <button type="button" z-button zPopover zTrigger="hover" zType="outline" [zContent]="popoverContent">
      Hover me
    </button>

    <ng-template #popoverContent>
      <z-popover>
        <div z-popover-header>
          <h4 z-popover-title>Hover content</h4>
          <p z-popover-description>This popover appears when you hover over the button.</p>
        </div>
      </z-popover>
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverHoverComponent {}

interactive

Control the popover programmatically through show(), hide() and toggle().
import { ChangeDetectionStrategy, Component, signal, viewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardInputComponent } from '@/shared/components/input/input.component';
import { ZardPopoverDirective } from '@/shared/components/popover/popover.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';

@Component({
  selector: 'z-demo-popover-interactive',
  imports: [FormsModule, ZardButtonComponent, ZardInputComponent, ...ZardPopoverImports],
  template: `
    <button type="button" z-button zPopover zType="outline" [zContent]="interactiveContent" #popoverTrigger>
      Settings
    </button>

    <ng-template #interactiveContent>
      <z-popover>
        <div z-popover-header>
          <h4 z-popover-title>Settings</h4>
          <p z-popover-description>Manage your account settings.</p>
        </div>

        <div class="space-y-2">
          <label for="interactive-width" class="text-sm font-medium">Width</label>
          <input id="interactive-width" z-input type="text" placeholder="100%" class="w-full" [(ngModel)]="width" />
        </div>

        <div class="space-y-2">
          <label for="interactive-height" class="text-sm font-medium">Height</label>
          <input id="interactive-height" z-input type="text" placeholder="25px" class="w-full" [(ngModel)]="height" />
        </div>

        <button type="button" z-button class="w-full" zSize="sm" (click)="saveChanges()">Save changes</button>
      </z-popover>
    </ng-template>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverInteractiveComponent {
  readonly popoverDirective = viewChild.required('popoverTrigger', { read: ZardPopoverDirective });

  readonly width = signal('100%');
  readonly height = signal('25px');

  saveChanges() {
    console.log('Settings saved:', { width: this.width(), height: this.height() });
    this.popoverDirective().hide();
  }
}

API Reference

[zPopover]Component

The directive that creates a popover when applied to a trigger element.

PropertyDescriptionTypeDefault
[zTrigger] How the popover is triggered 'click' | 'hover' | null 'click'
[zContent] Required. Template to display in the popover TemplateRef<unknown> -
[zPlacement] Side of the trigger the popover opens on. `inline-start` and `inline-end` follow the text direction 'top' | 'bottom' | 'left' | 'right' | 'inline-start' | 'inline-end' 'bottom'
[zAlign] Alignment of the popover along the side of the trigger 'start' | 'center' | 'end' 'center'
[zSideOffset] Distance in pixels between the popover and the trigger number 4
[zAlignOffset] Offset in pixels along the alignment axis number 0
[zOrigin] Custom anchor element ElementRef -
[zVisible] Control visibility programmatically boolean false
[zOverlayClickable] Close on outside click boolean true
(zVisibleChange) Emits when visibility changes. Fires immediately, before the exit animation ends EventEmitter<boolean>

z-popoverComponent

The popover content. Exposes `data-side`, `data-align` and `data-open`/`data-closed` while it is mounted.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-popover-headerComponent

Groups the title and the description at the top of the popover.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-popover-titleComponent

The popover title. Wired to the content through `aria-labelledby`.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''

z-popover-descriptionComponent

The popover description. Wired to the content through `aria-describedby`.

PropertyDescriptionTypeDefault
[class] Additional CSS classes ClassValue ''
github iconwhatsapp icondiscord iconX icon

Made with in Brazil. Open source and available on GitHub .