Primitives
Display arbitrary content inside floating panels.
import { Component } from "@angular/core";
import { NgpButton } from "ng-primitives/button";
import {
NgpPopover,
NgpPopoverArrow,
NgpPopoverTrigger,
} from "ng-primitives/popover";
@Component({
selector: "app-popover",
imports: [NgpPopoverTrigger, NgpPopover, NgpPopoverArrow, NgpButton],
template: `
<button [ngpPopoverTrigger]="popover" ngpButton type="button">
Popover
</button>
<ng-template #popover>
<div ngpPopover>
<h3>Need Help?</h3>
<p>
Get quick tips and guidance on how to use this feature effectively.
Check out our documentation for more details!
</p>
<a target="_blank" href="https://www.youtube.com/watch?v=xvFZjo5PgG0"
>Learn More</a
>
<div ngpPopoverArrow></div>
</div>
</ng-template>
`,
styles: `
button {
padding-left: 1rem;
padding-right: 1rem;
border-radius: 0.5rem;
color: var(--ngp-text-primary);
outline: none;
height: 2.5rem;
font-weight: 500;
background-color: var(--ngp-background);
transition: background-color 300ms cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: var(--ngp-button-shadow);
}
button[data-hover] {
background-color: var(--ngp-background-hover);
}
button[data-focus-visible] {
outline: 2px solid var(--ngp-focus-ring);
}
button[data-press] {
background-color: var(--ngp-background-active);
}
[ngpPopover] {
position: absolute;
display: flex;
flex-direction: column;
row-gap: 4px;
max-width: 280px;
border-radius: 0.75rem;
background: var(--ngp-background);
padding: 0.75rem 1rem;
box-shadow: var(--ngp-shadow);
border: 1px solid var(--ngp-border);
outline: none;
animation: popover-show 0.1s ease-out;
transform-origin: var(--ngp-popover-transform-origin);
}
[ngpPopover][data-exit] {
animation: popover-hide 0.1s ease-out;
}
[ngpPopoverArrow] {
position: absolute;
pointer-events: none;
}
[ngpPopoverArrow][data-placement="top"] {
bottom: 0;
}
[ngpPopoverArrow][data-placement="bottom"] {
top: 0;
}
[ngpPopoverArrow]:before {
content: "";
display: block;
position: absolute;
left: 0;
bottom: 100%;
width: 0;
height: 0;
border: 6px solid transparent;
border-bottom-color: var(--ngp-background-inverse);
}
[ngpPopoverArrow]:after {
content: "";
display: block;
position: absolute;
left: 0;
bottom: 100%;
width: 0;
height: 0;
border: 6px solid transparent;
border-bottom-color: var(--ngp-background);
}
[ngpPopover] h3 {
font-size: 13px;
font-weight: 500;
margin: 0;
color: var(--ngp-text-primary);
}
[ngpPopover] p {
font-size: 13px;
margin: 0;
color: var(--ngp-text-secondary);
}
[ngpPopover] a {
font-size: 13px;
color: var(--ngp-text-blue);
text-decoration: none;
}
@keyframes popover-show {
0% {
opacity: 0;
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes popover-hide {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.9);
}
}
`,
})
export default class PopoverExample {}
Import the Popover primitives from ng-primitives/popover
.
import { NgpPopover, NgpPopoverTrigger, NgpPopoverArrow } from 'ng-primitives/popover';
Assemble the popover directives in your template.
<button [ngpPopoverTrigger]="popover" (ngpPopoverTriggerOpenChange)="onPopoverStateChange($event)">
Click me
</button>
<ng-template #popover>
<div ngpPopover>Popover content</div>
</ng-template>
You can listen to the ngpPopoverTriggerOpenChange
event to perform actions when the popover state changes. The event emits a boolean value indicating whether the popover is open or closed:
You can customize the offset using either a simple number or an object for more precise control:
<!-- Simple number offset -->
<button [ngpPopoverTrigger]="popover" ngpPopoverTriggerOffset="12">Popover with 12px offset</button>
<!-- Object offset for precise control -->
<button
[ngpPopoverTrigger]="popover"
[ngpPopoverTriggerOffset]="{mainAxis: 8, crossAxis: 4, alignmentAxis: 2}"
>
Popover with custom offset
</button>
Create a popover component that uses the NgpPopover
directive.
import { Component } from '@angular/core';
import { NgpButton } from 'ng-primitives/button';
import { PopoverTrigger } from './popover-trigger';
@Component({
selector: 'app-root',
imports: [NgpButton, PopoverTrigger],
template: `
<button ngpButton appPopoverTrigger="Popover content">Popover</button>
`,
styles: `
[ngpButton] {
padding-left: 1rem;
padding-right: 1rem;
border-radius: 0.5rem;
color: var(--ngp-text-primary);
outline: none;
height: 2.5rem;
font-weight: 500;
border: none;
background-color: var(--ngp-background);
transition: background-color 300ms cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: var(--ngp-button-shadow);
}
[ngpButton][data-hover] {
background-color: var(--ngp-background-hover);
}
[ngpButton][data-focus-visible] {
outline: 2px solid var(--ngp-focus-ring);
}
[ngpButton][data-press] {
background-color: var(--ngp-background-active);
}
`,
})
export default class App {}
The following directives are available to import from the ng-primitives/popover
package:
Apply the `ngpPopover` directive to an element that represents the popover. This typically would be a `div` inside an `ng-template`.[ngpPopover]
ngpPopover
Attribute | Description |
---|---|
data-enter |
Applied when the popover is being added to the DOM. This can be used to trigger animations. |
data-exit |
Applied when the popover is being removed from the DOM. This can be used to trigger animations. |
data-placement |
The final rendered placement of the popover. |
The following CSS custom properties are applied to the ngpPopover
directive:
Property | Description |
---|---|
--ngp-popover-transform-origin |
The transform origin of the popover for animations. |
--ngp-popover-trigger-width |
The width of the trigger element. |
Apply the `ngpPopoverTrigger` directive to an element that triggers the popover to show. Access the popover template ref.
Define if the trigger should be disabled.
Define the placement of the popover relative to the trigger.
Define the offset of the popover relative to the trigger.
Can be a number (applies to mainAxis) or an object with mainAxis, crossAxis, and alignmentAxis.
Define the delay before the popover is displayed.
Define the delay before the popover is hidden.
Define whether the popover should flip when there is not enough space for the popover.
Define the container in which the popover should be attached.
Define whether the popover should close when clicking outside of it.
Define whether the popover should close when the escape key is pressed.
Defines how the popover behaves when the window is scrolled.
Provide context to the popover. This can be used to pass data to the popover content.
Event emitted when the popover open state changes.
[ngpPopoverTrigger]
ngpPopoverTrigger
Attribute | Description |
---|---|
data-open |
Applied when the popover is open. |
data-disabled |
Applied when the popover is disabled. |
The NgpPopoverArrow
directive is used to add an arrow to the popover. It should be placed inside the popover content. It will receive inset-inline-start
or inset-block-start
styles to position the arrow based on the popover's placement. As a result it should be positioned absolutely within the popover content.
The arrow can be styled conditionally based on the popover's final placement using the data-placement
attribute:
[ngpPopoverArrow][data-placement='top'] {
/* Arrow styles when popover is positioned on top */
}
[ngpPopoverArrow][data-placement='bottom'] {
/* Arrow styles when popover is positioned on bottom */
}
[ngpPopoverArrow]
ngpPopoverArrow
Attribute | Description |
---|---|
data-placement |
The final rendered placement of the popover. |
For the popover to be positioned correctly relative to the trigger element, it must use absolute or fixed positioning. For example, you can use the following CSS:
[ngpPopover] {
position: absolute;
}
The ngpPopover
primitive adds a CSS custom property --ngp-popover-transform-origin
to the element that can be used to animate the popover from the trigger element.
The ngpPopover
will also add the data-enter
and data-exit
attributes to the element when it is being added or removed from the DOM. This can be used to trigger animations.
:host[data-enter] {
animation: fade-in 0.2s ease-in-out;
}
:host[data-exit] {
animation: fade-out 0.2s ease-in-out;
}
You can configure the default options for all popovers in your application by using the providePopoverConfig
function in a providers array.
import { providePopoverConfig } from 'ng-primitives/popover';
bootstrapApplication(AppComponent, {
providers: [
providePopoverConfig({
offset: 4,
placement: 'top',
showDelay: 0,
hideDelay: 0,
flip: true,
container: document.body,
closeOnOutsideClick: true,
scrollBehavior: 'reposition',
}),
],
});
Define the offset from the trigger element. Can be a number (applies to mainAxis) or an object with mainAxis, crossAxis, and alignmentAxis properties.
Number format: offset: 8
- Applies to mainAxis (distance from trigger)
Object format:
offset: {
mainAxis: 8, // Distance between popover and trigger element
crossAxis: 4, // Skidding along the alignment axis
alignmentAxis: 2 // Same as crossAxis but for aligned placements
}
Define the placement of the popover.
Define the delay before the popover shows.
Define the delay before the popover hides.
Define if the popover should flip when it reaches the edge of the viewport.
Define the container element for the popover. This is the document body by default.
Define whether the popover should close when clicking outside of it.
Defines how the popover behaves when the window is scrolled. If set to `reposition`, the popover will adjust its position automatically during scrolling. Make sure the popover uses `position: absolute` in this mode. If set to `block`, scrolling will be disabled while the popover is open. In this case, the popover should use `position: fixed`.
Copyright © 2025 Angular Primitives