Popovers
Elements provides a set of popover type components built on the native browser Popover API
Available Elements
- tooltip: contextual text only hints
- toggletip: contextual interactive hints
- dropdown: interactive navigation or form content
- dialog: large interactive content, interrupts user flow
- toast: contextual notification
- notification: async non contextual notification
- drawer: interactive navigation or extra contextual content
Installation
import '@nvidia-elements/core/dialog/define.js';
import '@nvidia-elements/core/drawer/define.js';
import '@nvidia-elements/core/dropdown/define.js';
import '@nvidia-elements/core/tooltip/define.js';
import '@nvidia-elements/core/toast/define.js';
import '@nvidia-elements/core/notification/define.js';
Interactive showcase of all popover types (tooltip, toast, drawer, dropdown, dialog, notification) using modern popovertarget API.
<div nve-layout="row align:center gap:xl" style="height: 300px">
<nve-tooltip id="tooltip">hello there</nve-tooltip>
<nve-button popovertarget="tooltip">tooltip</nve-button>
<nve-toast id="toast" close-timeout="1500">copied!</nve-toast>
<nve-button popovertarget="toast">toast</nve-button>
<nve-drawer id="drawer" closable modal>
<nve-drawer-header>
<h3 nve-text="heading semibold sm">Title</h3>
</nve-drawer-header>
<p nve-text="body">some text content in a drawer</p>
</nve-drawer>
<nve-button popovertarget="drawer">drawer</nve-button>
<nve-dropdown id="dropdown" closable>
<h3 nve-text="heading">Title</h3>
<p nve-text="body">some text content in a dropdown</p>
</nve-dropdown>
<nve-button popovertarget="dropdown">dropdown</nve-button>
<nve-dialog id="dialog" closable modal>
<h3 nve-text="heading">Title</h3>
<p nve-text="body">some text content in a closable dialog</p>
</nve-dialog>
<nve-button popovertarget="dialog">dialog</nve-button>
<nve-notification id="notification" closable position="bottom" alignment="end" close-timeout="2000">
<h3 nve-text="label">notification</h3>
<p nve-text="body">some text content in a notification</p>
</nve-notification>
<nve-button popovertarget="notification">notification snackbar</nve-button>
</div>
Usage
<nve-tooltip id="tooltip">hello there</nve-tooltip>
<nve-button popovertarget="tooltip">button</nve-button>
Basic tooltip triggered by popovertarget attribute. Use for providing brief, contextual information on hover or focus without cluttering the interface.
<nve-tooltip id="tooltip">hello there</nve-tooltip>
<nve-button interestfor="tooltip">button</nve-button>
Events
Some popover types such as tooltip have complex interactions such as showing when hovered or focused. To simplify these interactions all popover type elements provide a open and close event to notify when the popover is attempting to close or open based on a user interaction. You can also listen
to the native toggle or beforetoggle popover events.
Event handling for tooltip lifecycle events. Useful for adding custom behavior when tooltip state changes.
<nve-tooltip id="tooltip">hello there</nve-tooltip>
<nve-button interestfor="tooltip">button</nve-button>
<script type="module">
const tooltip = document.querySelector("nve-tooltip");
tooltip.addEventListener("beforetoggle", () => console.log("beforetoggle"));
tooltip.addEventListener("toggle", () => console.log("toggle"));
tooltip.addEventListener("close", () => console.log("close"));
tooltip.addEventListener("open", () => console.log("open"));
</script>
Programmatic Trigger
You can trigger popovers manually via the native popover APIs.
Programmatic popover control with async operations before showing the popover.
<nve-toast hidden close-timeout="3000">hello there</nve-toast>
<nve-button>open after 2s</nve-button>
<script type="module">
const toast = document.querySelector("nve-toast");
const button = document.querySelector("nve-button");
button.addEventListener("click", async () => {
await new Promise((r) => setTimeout(r, 2000)); // do an async task then open the popover
toast.showPopover();
});
</script>
Positioning
All popovers follow the same position and alignment APIs. This allows popovers to position themselves relative to their anchor.
Fine-grained tooltip alignment combined with positioning for precise placement control. Use to align tooltips to specific edges of trigger elements, improving visual hierarchy and reducing overlap with other UI elements.
<nve-tooltip anchor="card" position="top" alignment="start">top start</nve-tooltip>
<nve-tooltip anchor="card" position="top">top center</nve-tooltip>
<nve-tooltip anchor="card" position="top" alignment="end">top end</nve-tooltip>
<nve-tooltip anchor="card" position="right" alignment="start">right start</nve-tooltip>
<nve-tooltip anchor="card" position="right">right center</nve-tooltip>
<nve-tooltip anchor="card" position="right" alignment="end">right end</nve-tooltip>
<nve-tooltip anchor="card" position="bottom" alignment="start">bottom start</nve-tooltip>
<nve-tooltip anchor="card" position="bottom">bottom center</nve-tooltip>
<nve-tooltip anchor="card" position="bottom" alignment="end">bottom end</nve-tooltip>
<nve-tooltip anchor="card" position="left" alignment="start">left start</nve-tooltip>
<nve-tooltip anchor="card" position="left">left center</nve-tooltip>
<nve-tooltip anchor="card" position="left" alignment="end">left end</nve-tooltip>
<nve-card id="card" style="width: 400px; height: 200px"></nve-card>
Anchor
The anchor property defines what element the popover should position itself relative to. For popovers like modals this defaults to the document body. The Anchor is optional as the popover uses the trigger to determine its anchor by default.
Anchor-based positioning where popovers anchor to specific elements rather than their triggers.
<nve-tooltip id="popover-1" anchor="btn-2">tooltip 1</nve-tooltip>
<nve-tooltip id="popover-2" anchor="btn-1">tooltip 2</nve-tooltip>
<div nve-layout="row gap:xs align:center">
<nve-button id="btn-1" popovertarget="popover-1">button 1</nve-button>
<nve-button id="btn-2" popovertarget="popover-2">button 2</nve-button>
</div>
For elements like tooltips this is often the same as anchor but the anchor and trigger are not always the same as UX patterns such as guided tours may have trigger elements that are not the anchored position. Both anchor and trigger can take a string idref or a DOM Element reference.
Closable
Many of the popovers provide a closable property option. This enables a close button on the popover. When using native HTML popover APIs, it's important to understand how they handle closure via the escape key.
Unlike some third-party solutions, browser-native popovers disable escape key prevention for closure by design, ensuring a seamless user experience. Similarly, native dialogs follow a similar pattern (chromium): only the first press of the escape key can prevent the dialog from closing,
while later presses have no effect. This behavior is for accessibility, preventing a popover from closing can prevent the user from navigating the page and block functionality.
Simple modal dialog with keyboard accessibility, including escape key to close functionality.
<nve-button popovertarget="popover">open dialog</nve-button>
<nve-dialog id="popover" modal>
<p nve-text="body">Press "escape" key to close.</p>
</nve-dialog>
Nested
Nested popovers within dialogs and notifications, with proper layering and stacking order.
<style>
#root-inner {
height: 2000px;
}
</style>
<nve-dialog closable modal>
<h3 nve-text="heading">Dialog</h3>
<p nve-text="body" style="margin-bottom: 48px">hello there</p>
<nve-button id="dropdown-btn">search</nve-button>
<nve-dropdown anchor="dropdown-btn" closable position="bottom" alignment="start">
<nve-search rounded>
<label>search dataset</label>
<nve-icon-button
id="tooltip-btn"
icon-name="information-circle-stroke"
container="flat"
aria-label="more details"
slot="label"
></nve-icon-button>
<input type="search" placeholder="search" />
</nve-search>
<nve-tooltip anchor="tooltip-btn" position="top">hello there</nve-tooltip>
</nve-dropdown>
</nve-dialog>
<nve-notification closable position="bottom" alignment="end">
<h3 nve-text="label">notification</h3>
<p nve-text="body">some text content in a notification</p>
</nve-notification>
Event Bubbling
Custom events like many standard DOM events bubble. When listening to larger DOM trees, check which element dispatched the source event.
<div style="width: 100vw; height: 100vh">
<p nve-text="body" id="event-example">event:</p>
<nve-button popovertarget="event-example-dialog">open</nve-button>
<nve-dialog id="event-example-dialog" closable modal (close)="close($event)" (open)="open($event)">
<nve-dialog-header>Header</nve-dialog-header>
<div>
<nve-button popovertarget="event-example-dropdown">show dropdown</nve-button>
</div>
<nve-dropdown id="event-example-dropdown"> dropdown content </nve-dropdown>
</nve-dialog>
</div>
<script type="module">
const dialog = document.querySelector("#event-example-dialog");
const eventLabel = document.querySelector("#event-example");
// Custom events like many standard DOM events bubble. By listening to
// larger DOM trees check which element dispatched the source event
dialog.addEventListener("open", (e) => {
eventLabel.innerText = "open: " + e.target.localName;
});
dialog.addEventListener("close", (e) => {
eventLabel.innerText = "close: " + e.target.localName;
});
</script>
Invoker Commands
The Invoker Command APIs can also dispatch command events to show and hide popovers.
Use the commandfor and command attributes to trigger custom Invoker Commands, such as toggling a dialog.
<nve-button commandfor="dialog" command="toggle-popover">toggle popover</nve-button>
<nve-dialog id="dialog" modal closable>
<nve-dialog-header>
<h3 nve-text="heading semibold">title</h3>
</nve-dialog-header>
<p nve-text="body">some text content in a closable dialog</p>
</nve-dialog>
Shadow Root Anchoring
Native CSS Anchor Positioning allows two elements to tether together via a unique identifier. This is commonly used for popover-like elements. CSS Anchor Positioning recently enabled cross root associations. https://github.com/w3c/csswg-drafts/issues/9408.
But the declarative native popover API requires popovers and the trigger/source to still exist within the same render root and not separate by Shadow DOM. Elements attempts to bridge this gap by traversing and making the association for the trigger and popover. This is not 100% reliable, in general popovers should ideally be within the same render root for best performance.