NativePopover
Trigger, surface and close parts around the native Popover API. 0 kB client JS, at most 0.5 kB gzipped.
Anatomy
<NativePopover.Trigger target="actions">Actions</NativePopover.Trigger>
<NativePopover.Surface id="actions">
<NativePopover.Close target="actions">Close</NativePopover.Close>
</NativePopover.Surface>Live demo
This demo also runs with JavaScript disabled.
Anchored below the trigger.
NativePopover.Trigger
Renders <button> (via `createElement`). Emits popoverTarget, popoverTargetAction, type="button", class="uiify-native-popover-trigger".
| Prop | Type | Default | Description |
|---|---|---|---|
target | string | — | Required. `id` of the `NativePopover.Surface` this button acts on. |
action | "toggle" | "show" | "hide" | "toggle" | What this trigger does to its target popover when activated. |
as | NativeButtonAs<TAs> | "button" | See NativeInvoker. |
NativePopover.Surface
Renders <div popover>. Emits popover={mode}, data-side, data-align, data-positioning="native", class="uiify-native-popover-surface".
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | — | Required. Referenced by trigger and close `target`. |
mode | "auto" | "manual" | "auto" | The cross-engine popover baseline; `hint` is not part of this contract. |
side | "top" | "right" | "bottom" | "left" | — | Anchor side. |
align | "start" | "center" | "end" | — | Anchor alignment. |
NativePopover.Close
Renders <button> (via `createElement`). Emits popoverTargetAction="hide", class="uiify-native-popover-close".
| Prop | Type | Default | Description |
|---|---|---|---|
target | string | — | Required. `id` of the `NativePopover.Surface` this button hides. |
as | NativeButtonAs<TAs> | "button" | See NativeInvoker. |
Rendered DOM
<button popoverTarget="actions" popoverTargetAction="toggle" type="button" class="uiify-native-popover-trigger">Actions</button><div data-align="center" data-positioning="native" data-side="bottom" popover="auto" id="actions" class="uiify-native-popover-surface"><button popoverTarget="actions" popoverTargetAction="hide" type="button" class="uiify-native-popover-close">Close</button></div>CSS contract
[popover]:popover-open {
/* true while the surface is shown */
}
/* the element sets data-side/data-align from side/align — style off these, not JS */Build your own
import { NativePopover } from "@gunkin/uiify/elements/native-popover";
export function ActionMenu() {
return (
<>
<NativePopover.Trigger target="row-actions">Actions</NativePopover.Trigger>
<NativePopover.Surface aria-label="Row actions" id="row-actions">
<button type="button">Rename</button>
<button type="button">Duplicate</button>
<button type="button">Delete</button>
</NativePopover.Surface>
</>
);
}
Used by
Gotchas
- `id` must be stable across server and client renders — `NativePopover.Trigger`/`NativePopover.Close`'s `target` has to match it exactly.
- `NativePopover.Surface` has no implicit ARIA role, so — unlike `NativeDialog.Content` — it has no required accessible name.
- `renderToStaticMarkup` keeps `popoverTarget`/`popoverTargetAction` camelCase in the raw HTML string (React doesn't special-case them for static rendering the way it does `command`/`commandfor`) — real browser behavior and hydration are unaffected, but don't be surprised reading the SSR output.
Import
import { NativePopover } from "@gunkin/uiify/elements/native-popover";