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, accepts isOpen / setIsOpen and configures alignment and offset.
  • PopOverTrigger — the anchored element. Wraps any focusable child in a keyboard-accessible role="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:

KeyAction
Enter / SpaceToggles 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

PropTypeDefaultDescription
isOpen*booleanWhether the floating panel is visible.
setIsOpen*(isOpen: boolean) => voidCalled to open or close the panel.
alignment"left" | "right""left"Horizontal anchor of the floating panel relative to the trigger.
offsetnumber8Gap in px between trigger bottom and floating panel top.
classNamestringundefinedAdditional class on the root container.
children*React.ReactNodeMust include exactly one PopOverTrigger and one PopOverContent.

* Required

PopOverTrigger

PropTypeDefaultDescription
children*React.ReactNodeThe element the floating panel anchors to. Should be a single focusable element such as a Button or an Input.
classNamestringundefinedAdditional class on the trigger wrapper. Useful for sizing (e.g. "w-full") or styling the anchor element.

* Required

PopOverContent

PropTypeDefaultDescription
children*React.ReactNodeContent rendered inside the floating panel.
classNamestringundefinedAdditional class on the floating panel div.

* Required

Design Guidelines

  • Use PopOver for contextual content that belongs near its trigger — date pickers, quick forms, action menus.
  • Use Modal instead when the content requires full attention or destroys page context.
  • Close the popover from inside onChange or action handlers when the user completes the action.
  • Avoid putting long scrolling lists inside PopOverContent — reach for Dropdown or MultiSelect instead.

Accessibility

  • The trigger renders as role="button" with tabIndex={0} and aria-haspopup="dialog" / aria-expanded reflecting the state.
  • Clicking the trigger toggles the panel; Enter and Space do the same for keyboard users.
  • Outside click and Escape close the panel.
  • The panel repositions automatically on scroll and resize, and flips above the trigger when it would overflow the viewport.