Popover
A floating panel on Ark UI's Popover — portaled, width-matched to the trigger, animated, with composable Header / Body / Footer slots.
Popover is the kit's floating panel, a styled layer over Ark UI's Popover. Ark owns the open/close state, positioning, focus and Esc / click-outside; the kit adds the portaled, animated panel chrome and three composable slots. It's a panel, not a menu — you render your own rows inside Body, so it hosts anything: a custom picker, a filter popover, the TreeMultiSelect tree.
import { Popover } from '@42-central/ui-react/popover';
<Popover.Root open={open} onOpenChange={(d) => setOpen(d.open)}>
<Popover.Trigger asChild>
<Button>Open</Button>
</Popover.Trigger>
<Popover.Content>
<Popover.Header>…</Popover.Header> {/* fixed */}
<Popover.Body>…</Popover.Body> {/* scrolls */}
<Popover.Footer>…</Popover.Footer> {/* fixed */}
</Popover.Content>
</Popover.Root>Basic usage
open / onOpenChange make it controlled. Header and Footer stay fixed while Body scrolls; the panel is portaled, so it escapes overflow: hidden ancestors, and its height tracks the available space up to a cap.
Narrow the list down to what you're looking for.
Code
const [open, setOpen] = useState(false);
<Popover.Root open={open} onOpenChange={(d) => setOpen(d.open)}>
<Popover.Trigger asChild>
<Button>Open popover</Button>
</Popover.Trigger>
<Popover.Content>
<Popover.Header>Filters</Popover.Header>
<Popover.Body className="flex flex-col gap-3">
<Text size="sm" c="muted">Narrow the list down to what you're looking for.</Text>
<Switch label="Show archived" size="sm" />
<Switch label="Only mine" size="sm" defaultChecked />
</Popover.Body>
<Popover.Footer>
<Button size="xs" variant="subtle">Reset</Button>
<Button size="xs">Apply</Button>
</Popover.Footer>
</Popover.Content>
</Popover.Root>Positioning
width="target" (default) matches the trigger's width; position sets the placement and offset the gap. The panel flips and shifts to stay in view (Ark / Floating UI).
Narrow the list down to what you're looking for.
Narrow the list down to what you're looking for.
Narrow the list down to what you're looking for.
Code
<Popover.Root position="bottom-start" width="target">…</Popover.Root>
<Popover.Root position="top" width="auto" offset={8}>…</Popover.Root>Hiding when detached
By default, the panel hides itself once its trigger is fully clipped by its
own scrolling ancestor — no separate open management needed, it stays hidden
(not closed) and reappears the moment the trigger scrolls back into view.
Useful for a trigger that lives inside a scrollable list or grid, where a
floating panel that keeps following the trigger's (now off-screen) coordinates
would otherwise drift or overlap unrelated content. Pass
hideWhenDetached={false} to opt out and keep the old "follow the trigger's
last known position" behavior.
Scroll the box — I'll hide once my trigger leaves view.
Code
<Popover.Root>…</Popover.Root> // hides on scroll-out by defaultHover mode
mode="hover" adds a delayed hover preview on top of the click — the "hover a login, see a profile card" pattern, with content people can act on. Hovering the trigger opens a light preview; a click, a tap or Enter pins it as a regular popover. A click on an open preview pins it rather than closing it, and so does pressing or focusing anything inside it.
Code
<Popover.Root mode="hover" width="auto">
<Popover.Trigger asChild>
<button type="button">
<Avatar name="Farid Karim" color="name" size="sm" />
fkarim
</button>
</Popover.Trigger>
<Popover.Content className="flex w-72 flex-col gap-3 p-3">
<div className="flex items-center gap-3">
<Avatar name="Farid Karim" color="name" />
<div className="flex min-w-0 flex-1 flex-col">
<Text size="sm" span className="font-medium">Farid Karim</Text>
<Text size="xs" c="muted" span>fkarim · Paris · e2r4p11</Text>
</div>
<Copy value="fkarim" variant="subtle" color="neutral" size="sm" />
</div>
<Button asChild size="xs" variant="light" color="neutral">
<a href="/users/fkarim">View profile</a>
</Button>
</Popover.Content>
</Popover.Root>| Input | Preview | Pinned |
|---|---|---|
| Mouse | Hover the trigger for openDelay (600ms) to open; leave both the trigger and the panel for closeDelay (300ms) to close. Crossing from one to the other keeps it open. | Click the trigger, or press inside the preview. Pointer-leave no longer closes it. |
| Touch | — (touch has no hover) | Tap the trigger. |
| Keyboard | — | Enter or Space on the trigger, or Tab into an open preview. |
| Focus | Never moved, never restored. | Moves into the panel, and back to the trigger on close. |
| Closes on | Leaving, Escape, outside click. | A second click on the trigger, Escape, outside click, Tab out. |
A preview is never modal; a pinned panel takes your modal / autoFocus / restoreFocus as usual. Make the trigger a button, not a link: the click belongs to the popover, so put the link inside the card. For a pure, non-interactive link preview — where the trigger is the link — HoverCard is the right tool. openDelay and closeDelay only exist with mode="hover"; on a click popover they're a type error.
Anatomy
Popover.Root— wraps Ark'sPopover.Root; owns open state + positioning.Popover.Trigger— the trigger; useasChildto wrap your own element.Popover.Content— the portaled, animated panel.Popover.Header/Popover.Footer— fixed, non-scrolling slots.Popover.Body— the scroll region; render your rows (and any empty message) here.
testId
Popover.Root is a headless provider with no DOM node of its own, so it takes no testId. Popover.Content — the real floating panel — takes its own testId, as do Popover.Header, Popover.Footer, and Popover.Body, each landing on its own rendered element. Popover.Trigger wraps Ark's trigger and passes every prop through, so an arbitrary data-testid lands on it natively. See Testing.
API
Popover.Root props (beyond Ark's Popover.Root):
Prop
Type
Changelog
- #294
asChildis a compile error onCombobox.Control,Group,Content,PanelandItem,Drawer/Modal.Positioner,Drawer.Grabber,Popover.ContentandTooltip.Arrow
- #209
Popovers open beside the chip inside the view's own scroller and mount lazily; Popover accepts Ark's full positioning options
- #135
Opened inside a
ModalorDrawer, an overlay portals into it, so it always paints above its host
- #85
hideWhenDetached, on by default, hides the panel once its trigger scrolls out of view
- #41
Floating surfaces take a brand tint
- #37
Borders and dividers on floating panels match the panel's own surface
Popover: a click-triggered panel
Menu
A dropdown of actions on Ark UI's Menu — composable parts plus a typesafe data-driven form, with sections, submenus, checkbox / radio items, and a context-menu trigger.
Tooltip
A hover/focus hint on Ark UI's Tooltip — ARIA-wired (role="tooltip" + aria-describedby), portaled and animated. Drops into running text or wraps any element.