Popover
Popovers anchor rich content to a trigger element (filters, mini forms, or contextual details) without modal focus trapping.
Try it
Show items from the last 7 days.
Usage
Wrap content in RuiPopoverTrigger and RuiPopoverContent. Control open, placement, and portal for positioning.
import { RuiPopover, RuiPopoverTrigger, RuiPopoverContent } from '@ecopages/radiant-ui/popover';
import { RuiButton } from '@ecopages/radiant-ui/button';
<RuiPopover placement="bottom-start">
<RuiPopoverTrigger>
<RuiButton variant="outline">Filter</RuiButton>
</RuiPopoverTrigger>
<RuiPopoverContent>
<p>Show items from the last 7 days.</p>
</RuiPopoverContent>
</RuiPopover>Listbox variant
Set variant="listbox" when the popover hosts a RuiListbox; width and focus behavior adapt automatically.
Match anchor width
Enable matchAnchorWidth for select-style popovers so the panel aligns with the trigger.
Theming
The popover surface maps to semantic surface roles, never Tailwind palette steps:
| Part | CSS roles |
|---|---|
Surface (rui-popover) | background, on-background, border, rounded-container, shadow-overlay |
| Listbox variant | Stripped padding for embedded listboxes |
| Z-order | popover (below dialogs, above page content) |
Overrides go at the theme layer, not in component CSS.
Accessibility
- Triggers expose
aria-expandedwhen the popover is open. - Content is focusable and dismissible with Escape.
- Do not trap focus; users should reach surrounding content without closing first.
API
RuiPopover is a custom element (<rui-popover>) positioning a floating role="dialog" surface. RuiPopoverTrigger (<rui-popover-trigger>) coordinates open state with the trigger click.
Attributes (<rui-popover>)
| Attribute | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the popover is open (controlled). |
placement | RuiPlacement | bottom | Placement relative to the anchor. |
portal | boolean | true | Teleport the surface to document.body. |
match-anchor-width | boolean | false | Match the anchor width (dropdown menus). |
offset | number | 8 | Gap between anchor and surface in px. |
anchor | string | '' | CSS selector for an external anchor. |
variant | default · listbox | default | Surface variant; listbox strips padding. |
Attributes (<rui-popover-trigger>)
| Attribute | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the popover starts open. |
Slots
| Component | Slot | Description |
|---|---|---|
RuiPopover | trigger | Pressable anchor (when not using an external anchor). |
RuiPopover | content | Popover body rendered inside the floating surface. |
RuiPopoverTrigger | trigger | Pressable element that toggles the child popover. |
RuiPopoverTrigger | (default) | The child <rui-popover>. |
Events
| Event | Detail | Description |
|---|---|---|
rui-open-change | { open: boolean } | Emitted when open state changes. |
CSS classes
Public BEM classes (documented via @cssclass):
| Class | Description |
|---|---|
.rui-popover-host | Anchor + surface wrapper. |
.rui-popover | Floating surface (role="dialog"); background + rounded-container + shadow-overlay. |
.rui-popover--listbox | Stripped padding for embedded listboxes. |
.rui-popover-trigger | Trigger + popover wrapper. |
Theme roles
| Part | CSS variables consumed |
|---|---|
| Surface | --background, --on-background, --border, --radius-container, --shadow-overlay |
| Z-order | --z-popover |