Server Component · 0 KB client JS

Popout

An anchored disclosure panel that ships zero JavaScript. The trigger is a real <button popovertarget>; the panel is a native [popover] positioned by CSS anchor positioning against the trigger's implicit anchor. The platform supplies everything a JS popover engine used to: the top layer, light dismiss, Esc, focus return, and collision flipping. It renders as a Server Component and works with JavaScript disabled.

A zero-JS disclosure: top layer, Esc, light dismiss, and collision flipping all come from the platform.

Import#

import { Popout } from "astralis-ui";

Placement#

side and align compile to position-area; when there is no room, the panel flips via position-try-fallbacks, the browser's collision handling, not a resize listener:

Anchored top; flips when there is no room.
Anchored bottom; flips when there is no room.
Anchored left; flips when there is no room.
Anchored right; flips when there is no room.

Popout is a disclosure, not a menu#

Deliberately: there is no roving focus and no menu ARIA, because those require state CSS cannot own. For an actions menu with arrow-key navigation, reach for Menu. That's what it's for. Popout is the right shape for nav dropdowns, info panels, and filters.

Browser support#

Popover is Baseline 2025; anchor positioning is cross-engine since January 2026 (Chrome 125+, Safari 26+, Firefox 147+). In browsers without anchor positioning the panel still opens, closes and light-dismisses correctly, and falls back to the browser's centered top-layer placement.

Props#

The root takes only its two parts; the panel is configured on Popout.Content:

PropTypeDefaultDescription
side"top" | "bottom" | "left" | "right""bottom"Which side of the trigger the panel opens on.
align"start" | "center" | "end""start"Alignment along that side.
sideOffsetnumber8Gap between the trigger and the panel, in pixels.

Parts#

PropTypeDefaultDescription
Popout.Triggerbutton propsNoneRenders a button wired to the panel with popovertarget.
Popout.Contentdiv props, plus the props aboveNoneThe native popover panel.

Keyboard#

All of it is the browser's popover behavior:

KeyAction
Enter / Space on the triggerOpen or close the panel
EscClose the panel and return focus to the trigger
TabMove through the panel's content, then on through the page