PopOver
PopOver is a compound component that anchors floating content to a trigger element. It is composed of three parts that share state via context:
PopOver— controlled state holder, acceptsisOpen/setIsOpenand configuresalignmentandoffset.PopOverTrigger— the anchored element. Wraps any focusable child in a keyboard-accessiblerole="button"div.PopOverContent— portal-mounted floating panel. Auto-flips above the trigger when it would overflow the viewport, and clamps to the screen edges.
The panel closes on outside click and on Escape, and reposition itself on scroll and resize.
Import
import { PopOver, PopOverTrigger, PopOverContent } from "h2o-library";Usage
Basic
Basic PopOver
With Form
With Rich Content
const [isOpen, setIsOpen] = useState(false);
<PopOver isOpen={isOpen} setIsOpen={setIsOpen}>
<PopOverTrigger>
<Button label="Open PopOver" />
</PopOverTrigger>
<PopOverContent>
<div className="p-4">
<p>Floating content here.</p>
</div>
</PopOverContent>
</PopOver>;Alignment
alignment="right" aligns the panel's right edge with the trigger's right edge — useful when the trigger is flush with the left edge of viewport.
<PopOver isOpen={open} setIsOpen={setOpen} alignment="right">
<PopOverTrigger>
<Button icon="more-vertical" iconOnly tooltip="Actions" />
</PopOverTrigger>
<PopOverContent>{/* … */}</PopOverContent>
</PopOver>;Offset
offset controls the vertical gap (in px) between the trigger and the panel. Default is 8.
<PopOver isOpen={open} setIsOpen={setOpen} offset={4}>
{/* tighter spacing */}
</PopOver>;Playground
Loading playground…
Keyboard
PopOverTrigger is a focusable role="button" wrapper:
| Key | Action |
|---|---|
Enter / Space | Toggles the panel open/closed. |
When the panel is open, Escape closes it (document listener). Focus behavior inside PopOverContent follows whatever focusable elements you render.
Props
PopOver
| Prop | Type | Default | Description |
|---|---|---|---|
isOpen* | boolean | — | Whether the floating panel is visible. |
setIsOpen* | (isOpen: boolean) => void | — | Called to open or close the panel. |
alignment | "left" | "right" | "left" | Horizontal anchor of the floating panel relative to the trigger. |
offset | number | 8 | Gap in px between trigger bottom and floating panel top. |
className | string | undefined | Additional class on the root container. |
children* | React.ReactNode | — | Must include exactly one PopOverTrigger and one PopOverContent. |
* Required
PopOverTrigger
| Prop | Type | Default | Description |
|---|---|---|---|
children* | React.ReactNode | — | The element the floating panel anchors to. Should be a single focusable element such as a Button or an Input. |
className | string | undefined | Additional class on the trigger wrapper. Useful for sizing (e.g. "w-full") or styling the anchor element. |
* Required
PopOverContent
| Prop | Type | Default | Description |
|---|---|---|---|
children* | React.ReactNode | — | Content rendered inside the floating panel. |
className | string | undefined | Additional class on the floating panel div. |
* Required
Design Guidelines
- Use
PopOverfor contextual content that belongs near its trigger — date pickers, quick forms, action menus. - Use
Modalinstead when the content requires full attention or destroys page context. - Close the popover from inside
onChangeor action handlers when the user completes the action. - Avoid putting long scrolling lists inside
PopOverContent— reach forDropdownorMultiSelectinstead.
Accessibility
- The trigger renders as
role="button"withtabIndex={0}andaria-haspopup="dialog"/aria-expandedreflecting the state. - Clicking the trigger toggles the panel;
EnterandSpacedo the same for keyboard users. - Outside click and
Escapeclose the panel. - The panel repositions automatically on scroll and resize, and flips above the trigger when it would overflow the viewport.