Actions
Popover
A nonmodal panel anchored to a button, for a small form, setting or detail.
| Class name | Type | Description |
|---|---|---|
popoverplf:popover | Component | Wraps the trigger and the panel |
popover-contentplf:popover-content | Part | The floating panel |
popover-titleplf:popover-title | Part | The panel's heading |
popover-descriptionplf:popover-description | Part | Muted text under the title |
Default
A panel without a heading needs aria-label or aria-labelledby; without a name the popover gets no behavior.
Reading preferences
Choose how articles appear in this preview.
<div class="popover" data-plf-popover data-plf-align="start">
<button class="btn btn-outline">Reading preferences</button>
<div class="popover-content" aria-describedby="popover-default-description" hidden>
<h3 class="popover-title">Reading preferences</h3>
<p class="popover-description" id="popover-default-description">Choose how articles appear in this preview.</p>
<form class="flex flex-col gap-4">
<div class="field">
<label class="field-label" for="popover-default-width">Reading width</label>
<select class="select" id="popover-default-width" data-plf-select>
<option>Comfortable</option>
<option>Compact</option>
<option>Wide</option>
</select>
</div>
<label class="choice">
<input class="checkbox" type="checkbox" checked>
<span class="choice-label">Show article summaries</span>
</label>
<div class="flex flex-wrap items-center gap-4">
<button class="btn btn-primary btn-sm" type="submit">Apply</button>
<button class="btn btn-outline btn-sm" type="button" data-plf-popover-close>Cancel</button>
</div>
</form>
</div>
</div><div class="plf:popover" data-plf-popover data-plf-align="start">
<button class="plf:btn plf:btn-outline">Reading preferences</button>
<div class="plf:popover-content" aria-describedby="popover-default-description" hidden>
<h3 class="plf:popover-title">Reading preferences</h3>
<p class="plf:popover-description" id="popover-default-description">Choose how articles appear in this preview.</p>
<form class="plf:flex plf:flex-col plf:gap-4">
<div class="plf:field">
<label class="plf:field-label" for="popover-default-width">Reading width</label>
<select class="plf:select" id="popover-default-width" data-plf-select>
<option>Comfortable</option>
<option>Compact</option>
<option>Wide</option>
</select>
</div>
<label class="plf:choice">
<input class="plf:checkbox" type="checkbox" checked>
<span class="plf:choice-label">Show article summaries</span>
</label>
<div class="plf:flex plf:flex-wrap plf:items-center plf:gap-4">
<button class="plf:btn plf:btn-primary plf:btn-sm" type="submit">Apply</button>
<button class="plf:btn plf:btn-outline plf:btn-sm" type="button" data-plf-popover-close>Cancel</button>
</div>
</form>
</div>
</div>import { useId } from 'react';
import { Button, buttonClass, Checkbox, Field, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Select } from '@pelatform/react';
export function Example() {
const description = useId();
return (
<Popover align="start">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Reading preferences</PopoverTrigger>
<PopoverContent aria-describedby={description}>
<PopoverTitle>Reading preferences</PopoverTitle>
<PopoverDescription id={description}>Choose how articles appear in this preview.</PopoverDescription>
<form className="flex flex-col gap-4">
<Field label="Reading width">
<Select custom>
<option>Comfortable</option>
<option>Compact</option>
<option>Wide</option>
</Select>
</Field>
<label className="choice">
<Checkbox defaultChecked />
<span className="choice-label">Show article summaries</span>
</label>
<div className="flex flex-wrap items-center gap-4">
<Button color="primary" size="sm" type="submit">Apply</Button>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })} type="button">Cancel</PopoverClose>
</div>
</form>
</PopoverContent>
</Popover>
);
}import { useId } from 'react';
import { Button, buttonClass, Checkbox, Field, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Select } from '@pelatform/react/prefixed';
export function Example() {
const description = useId();
return (
<Popover align="start">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Reading preferences</PopoverTrigger>
<PopoverContent aria-describedby={description}>
<PopoverTitle>Reading preferences</PopoverTitle>
<PopoverDescription id={description}>Choose how articles appear in this preview.</PopoverDescription>
<form className="plf:flex plf:flex-col plf:gap-4">
<Field label="Reading width">
<Select custom>
<option>Comfortable</option>
<option>Compact</option>
<option>Wide</option>
</Select>
</Field>
<label className="plf:choice">
<Checkbox defaultChecked />
<span className="plf:choice-label">Show article summaries</span>
</label>
<div className="plf:flex plf:flex-wrap plf:items-center plf:gap-4">
<Button color="primary" size="sm" type="submit">Apply</Button>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })} type="button">Cancel</PopoverClose>
</div>
</form>
</PopoverContent>
</Popover>
);
}Sharing details
Opens on top.
Sharing details
Opens on the right.
Sharing details
Opens at the bottom.
Sharing details
Opens on the left.
Sharing details
Aligned to the start of the button.
Sharing details
Aligned to the end of the button.
<div class="popover" data-plf-popover data-plf-placement="top">
<button class="btn btn-outline">Top</button>
<div class="popover-content" hidden>
<h3 class="popover-title">Sharing details</h3>
<p class="popover-description">Opens on top.</p>
<button class="btn btn-outline btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="popover" data-plf-popover data-plf-placement="right">
<button class="btn btn-outline">Right</button>
<div class="popover-content" hidden>
<h3 class="popover-title">Sharing details</h3>
<p class="popover-description">Opens on the right.</p>
<button class="btn btn-outline btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="popover" data-plf-popover>
<button class="btn btn-outline">Bottom</button>
<div class="popover-content" hidden>
<h3 class="popover-title">Sharing details</h3>
<p class="popover-description">Opens at the bottom.</p>
<button class="btn btn-outline btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="popover" data-plf-popover data-plf-placement="left">
<button class="btn btn-outline">Left</button>
<div class="popover-content" hidden>
<h3 class="popover-title">Sharing details</h3>
<p class="popover-description">Opens on the left.</p>
<button class="btn btn-outline btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="popover" data-plf-popover data-plf-align="start">
<button class="btn btn-outline">Bottom start</button>
<div class="popover-content" hidden>
<h3 class="popover-title">Sharing details</h3>
<p class="popover-description">Aligned to the start of the button.</p>
<button class="btn btn-outline btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="popover" data-plf-popover data-plf-align="end">
<button class="btn btn-outline">Bottom end</button>
<div class="popover-content" hidden>
<h3 class="popover-title">Sharing details</h3>
<p class="popover-description">Aligned to the end of the button.</p>
<button class="btn btn-outline btn-sm" data-plf-popover-close>Got it</button>
</div>
</div><div class="plf:popover" data-plf-popover data-plf-placement="top">
<button class="plf:btn plf:btn-outline">Top</button>
<div class="plf:popover-content" hidden>
<h3 class="plf:popover-title">Sharing details</h3>
<p class="plf:popover-description">Opens on top.</p>
<button class="plf:btn plf:btn-outline plf:btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="plf:popover" data-plf-popover data-plf-placement="right">
<button class="plf:btn plf:btn-outline">Right</button>
<div class="plf:popover-content" hidden>
<h3 class="plf:popover-title">Sharing details</h3>
<p class="plf:popover-description">Opens on the right.</p>
<button class="plf:btn plf:btn-outline plf:btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="plf:popover" data-plf-popover>
<button class="plf:btn plf:btn-outline">Bottom</button>
<div class="plf:popover-content" hidden>
<h3 class="plf:popover-title">Sharing details</h3>
<p class="plf:popover-description">Opens at the bottom.</p>
<button class="plf:btn plf:btn-outline plf:btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="plf:popover" data-plf-popover data-plf-placement="left">
<button class="plf:btn plf:btn-outline">Left</button>
<div class="plf:popover-content" hidden>
<h3 class="plf:popover-title">Sharing details</h3>
<p class="plf:popover-description">Opens on the left.</p>
<button class="plf:btn plf:btn-outline plf:btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="plf:popover" data-plf-popover data-plf-align="start">
<button class="plf:btn plf:btn-outline">Bottom start</button>
<div class="plf:popover-content" hidden>
<h3 class="plf:popover-title">Sharing details</h3>
<p class="plf:popover-description">Aligned to the start of the button.</p>
<button class="plf:btn plf:btn-outline plf:btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>
<div class="plf:popover" data-plf-popover data-plf-align="end">
<button class="plf:btn plf:btn-outline">Bottom end</button>
<div class="plf:popover-content" hidden>
<h3 class="plf:popover-title">Sharing details</h3>
<p class="plf:popover-description">Aligned to the end of the button.</p>
<button class="plf:btn plf:btn-outline plf:btn-sm" data-plf-popover-close>Got it</button>
</div>
</div>import { buttonClass, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from '@pelatform/react';
export function Example() {
return (
<>
<Popover placement="top">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Top</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens on top.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover placement="right">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Right</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens on the right.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover>
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Bottom</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens at the bottom.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover placement="left">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Left</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens on the left.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover align="start">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Bottom start</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Aligned to the start of the button.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover align="end">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Bottom end</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Aligned to the end of the button.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
</>
);
}import { buttonClass, Popover, PopoverClose, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger } from '@pelatform/react/prefixed';
export function Example() {
return (
<>
<Popover placement="top">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Top</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens on top.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover placement="right">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Right</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens on the right.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover>
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Bottom</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens at the bottom.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover placement="left">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Left</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Opens on the left.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover align="start">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Bottom start</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Aligned to the start of the button.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
<Popover align="end">
<PopoverTrigger className={buttonClass({ variant: 'outline' })}>Bottom end</PopoverTrigger>
<PopoverContent>
<PopoverTitle>Sharing details</PopoverTitle>
<PopoverDescription>Aligned to the end of the button.</PopoverDescription>
<PopoverClose className={buttonClass({ variant: 'outline', size: 'sm' })}>Got it</PopoverClose>
</PopoverContent>
</Popover>
</>
);
}JavaScript
Popover needs JavaScript. The Full script (pelatform-ui.min.js) starts every component on page load, with no code of your own; with ESM imports, call init() yourself, as in Installation. For markup you add later, see Dynamic content.
<link rel="stylesheet" href="pelatform-ui.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.min.css">
<link rel="stylesheet" href="components/button.min.css">
<link rel="stylesheet" href="components/form.min.css">
<link rel="stylesheet" href="components/popover.min.css">
<link rel="stylesheet" href="components/select.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/popover.min.js" defer></script>
<script src="components/select.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script><link rel="stylesheet" href="pelatform-ui.prefixed.min.css">
<script src="pelatform-ui.min.js" defer></script><link rel="stylesheet" href="core/pelatform-ui-core.prefixed.min.css">
<link rel="stylesheet" href="components/button.prefixed.min.css">
<link rel="stylesheet" href="components/form.prefixed.min.css">
<link rel="stylesheet" href="components/popover.prefixed.min.css">
<link rel="stylesheet" href="components/select.prefixed.min.css">
<script src="core/pelatform-ui-core.min.js" defer></script>
<script src="components/popover.min.js" defer></script>
<script src="components/select.min.js" defer></script>
<script>document.addEventListener('DOMContentLoaded', () => PLF.init());</script>| Attribute | Purpose |
|---|---|
data-plf-popover |
Marks the wrapper |
data-plf-popover-trigger |
Optional: marks the <button>, otherwise the wrapper’s direct child without hidden, or that child’s one button when it is a field, as in the pickers. disabled or aria-disabled="true" keeps it closed |
data-plf-popover-content |
Optional: marks the panel, otherwise the wrapper’s direct child with hidden. Its first heading names it unless it has aria-label or aria-labelledby |
data-plf-popover-close |
A button inside the panel that closes it |
data-plf-popover-anchor |
Optional wrapper around the trigger and companion fields; the panel aligns to it. Without it, a direct child that holds the trigger, such as a picker’s field, is the anchor |
data-plf-placement |
top, right, bottom (default) or left; flips to the opposite side when that has more room, and a left or right panel that fits on neither side opens below or above |
data-plf-align |
start, center (default) or end; start and end follow the text direction |
autofocus |
The control in the panel to focus on opening; otherwise the first one, or the panel |
data-plf-side |
Set by the script on the open panel to the side it opened on |
| Method | Behavior |
|---|---|
PLF. |
Returns the instance, creating it for valid markup |
PLF. |
Returns the instance or null |
instance. |
Opens and moves focus into the panel; returns false when already open, canceled or disabled |
instance. |
Closes and returns focus to the trigger; returns false when already closed or canceled |
instance. |
Opens or closes |
instance. |
Closes without events and removes listeners |
| Event | When |
|---|---|
plf: |
Before opening; cancelable |
plf: |
After opening |
plf: |
Before closing; cancelable |
plf: |
After closing |
Each event’s detail:
| Property | Value |
|---|---|
instance |
The Popover instance |
reason |
api, trigger or keyboard |
trigger |
The trigger or close button used, otherwise null |
| Key | Action |
|---|---|
| Enter, Space | On the trigger, opens or closes the panel |
| Tab, Shift+Tab | Move through the panel; leaving it closes the panel, without trapping focus |
| Escape | Closes the panel and returns focus to the trigger; inside a Modal, the next Escape closes the dialog |
React
Popover from @pelatform/react renders this wrapper around its parts and opens the panel as the script does; it keeps its own state, or the application sets it with open.
| Prop | Values | Default |
|---|---|---|
placement |
top, right, bottom, left |
bottom |
align |
start, center, end |
center |
open |
true or false, which the application changes in onOpenChange |
Its own state |
defaultOpen |
true starts open when the application does not set open |
false |
onOpenChange |
(open, reason), called when the panel opens or closes: trigger (the trigger, PopoverClose or a press outside) or keyboard (Escape, Tab or the focus leaving) |
None |
The parts:
| Part | Element |
|---|---|
PopoverTrigger |
A button with type="button" and aria-expanded; buttonClass gives it a look |
PopoverContent |
A div with popover-content and role="dialog", named by its PopoverTitle or an aria-label |
PopoverTitle |
An h3 with popover-title, or another heading with level |
PopoverDescription |
A p with popover-description |
PopoverClose |
A button that closes the panel and returns the focus to the trigger |
Other props are the element’s own, className and ref included.
| Variable | Default |
|---|---|
--plf-popover-width |
18rem, capped by the viewport |
--plf-popover-radius |
Large radius (8px) |
--plf-popover-bg, --plf-popover-color |
Elevated surface and text |
--plf-popover-border |
Subtle border |