Popover
A floating surface anchored to whatever element opened it, built entirely on the native Popover API.
Retention
Search reaches back ninety days on this plan. Older records stay exportable.
View code
<Button commandfor="result-filters" command="toggle-popover" variant="outline">
<SlidersHorizontalIcon />
Filters
</Button>
<Popover id="result-filters" placement="bottom">
{/* The surface brings a border, a radius and a shadow; the panel inside brings its
own padding, so a menu can still run its rows to the edge. */}
<div mix={[vstack({ gap: 4, align: "stretch" }), p(4)]}>
<Header mix={[m(0)]}>Filter results</Header>
<div mix={[vstack({ gap: 2, align: "stretch" })]}>
<Label htmlFor="filter-query">Contains</Label>
<Input id="filter-query" name="query" placeholder="middleware" />
</div>
<div mix={[hstack({ gap: 2, align: "end" })]}>
<div mix={[vstack({ gap: 2, align: "stretch" })]}>
<Label htmlFor="filter-from">From</Label>
<Input id="filter-from" name="from" type="date" />
</div>
<div mix={[vstack({ gap: 2, align: "stretch" })]}>
<Label htmlFor="filter-to">To</Label>
<Input id="filter-to" name="to" type="date" />
</div>
</div>
<Label mix={[hstack({ gap: 2, align: "center" })]}>
<input type="checkbox" name="archived" />
Include archived
</Label>
<Separator />
<div mix={[hstack({ gap: 2, justify: "end" })]}>
<Button commandfor="result-filters" command="hide-popover" variant="ghost" size="sm">
Cancel
</Button>
<Button type="submit" size="sm">
Apply
</Button>
</div>
</div>
</Popover>
<Button commandfor="retention-note" command="toggle-popover" variant="outline">
<InfoIcon />
Why is this limited?
</Button>
{/* "manual" keeps the panel open through an outside click, so a reader can go on
working with the page while it stays on screen. */}
<Popover id="retention-note" placement="top">
<div mix={[vstack({ gap: 3, align: "start" }), p(4)]}>
<p mix={[m(0), text("sm"), weight("medium")]}>Retention</p>
<p mix={[m(0), text("sm")]}>
Search reaches back ninety days on this plan. Older records stay exportable.
</p>
<Button commandfor="retention-note" command="hide-popover" variant="outline" size="sm">
Got it
</Button>
</div>
</Popover>Installation
An ordinary dependency: install it, import the theme once, and import the component where it is used.
npm add @sdxc/uiimport "@sdxc/ui/theme.css";Usage
import { Popover } from "@sdxc/ui";Renders the floating surface itself: a popover-attributed <div>
positioned via CSS anchor positioning. The invoker that opens it becomes
the implicit anchor its position-area resolves against.
Examples
<Popover id="filters" placement="top" mix={[fade({ duration: durations.normal })]}>
<OverlayArrow placement="top">
<svg width={12} height={12} viewBox="0 0 12 12">
<path d="M0 0 L6 6 L12 0" />
</svg>
</OverlayArrow>
{content}
</Popover>Props
Read from the component's own types, so every prop, every default and every allowed value is listed.
| Prop | Type | Description |
|---|---|---|
id | string | Stable id an invoker elsewhere on the page targets to open this host. |
popover? | "auto" | "hint" | "manual" | popover attribute mode: "auto" dismisses on outside click or Escape and closes sibling "auto" popovers when shown; "hint" layers over an open one without closing it; "manual" closes only when hidden explicitly. |
placement? | "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | Side of the anchor to render against. Defaults to DEFAULT_PLACEMENT. |
children | RemixNode | The popover's content. |
Also accepts everything in Omit<TagProps<"div">, "id" | "popover">.
Types
| Name | Values | What it decides |
|---|---|---|
Placement | "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "left-start" | "left-end" | "right" | "right-start" | "right-end" | Side of the anchor the host renders against, and, for the four corner variants, which anchor edge it aligns to along the perpendicular axis. |