Anchored popover shell with header, body, and footer slots for custom controls. Footer owns gapX and gapY, with FooterActions and FooterMeta for paired actions and status copy. Prefer this over Popover when the surface needs composed controls.
Also known as: rich popover, form popover, control popover, popover with controls, tool popover, custom popover, options popover, titled popover, options sheet
Install with the CLI, or copy the component source into your project.
Add options-panel (npm)
1npx --yes -p @default-file/ui df-ui add options-panel
Titled panel with Toggle Group choices, Input stepper fields, Switch, FooterActions, FooterMeta, and kit Badge.
Anchored popover shell with a titled header and open body/footer slots for custom controls such as inputs, buttons, and sections. Prefer this over Popover when the surface needs composed controls. Width hugs content up to the panel max-width token and the viewport. Shares Popover dismiss behavior: Escape and outside clicks close, and focus returns to the trigger.
Root open state. Same API as Popover.
| Prop | Type | Default | Notes |
|---|---|---|---|
| open | boolean | - | Controlled open state. |
| defaultOpen | boolean | - | Uncontrolled initial open state. |
| onOpenChange | (open: boolean) => void | - | Called when open state changes. |
| Prop | Type | Default | Notes |
|---|---|---|---|
| render | ReactElement | - | Host element that toggles the panel. |
| children | ReactNode | - | Trigger content when not using render. |
Panel shell. Sizes to content, capped by --df-options-panel-max-width and the viewport. Defaults to end align and bottom side.
| Prop | Type | Default | Notes |
|---|---|---|---|
| side | "top" | "bottom" | "left" | "right" | "bottom" | Preferred placement side. Flips when there is not enough space. |
| align | "start" | "center" | "end" | "auto" | "end" | Alignment along the side. auto picks the best fit from available space (content-aware). |
| sideOffset | number | 8 | Gap between trigger and panel. |
| matchTriggerWidth | boolean | false | Lock the panel to the trigger width. Off by default so the panel sizes to its content within the max-width cap. |
| portal | boolean | true | When false, render the panel inline for docs demos. The body hugs content with no height clamp. Default portals to document.body and keeps the body max-height scroll contract. |
| className | string | - | Override or extend panel chrome. Use width or max-width utilities when a call site needs a different size than the token default. |
| children | ReactNode | - | Compose OptionsPanelHeader, OptionsPanelBody, and OptionsPanelFooter. |
Header region with bottom border. Usually wraps the title.
| Prop | Type | Default | Notes |
|---|---|---|---|
| className | string | - | Additional styles on the header. |
| children | ReactNode | - | Usually OptionsPanelTitle. |
Panel title text.
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | - | Title copy. |
| className | string | - | Additional styles on the title. |
Body slot. Hugs content width. Never scrolls horizontally. Portaled panels clamp height with --df-options-panel-max-height and use the kit ScrollArea when content is taller. Inline panels (portal false) size to content with no scroll clamp.
| Prop | Type | Default | Notes |
|---|---|---|---|
| fit | boolean | false | Size the body to its content with no max-height scroll clamp. Default keeps the portaled height cap and vertical kit scroll. |
| className | string | - | Additional styles on the body. |
| children | ReactNode | - | Panel controls and content. |
Footer region for actions and meta. Column layout by default. gapY spaces stacked rows. gapX feeds FooterActions and FooterMeta.
| Prop | Type | Default | Notes |
|---|---|---|---|
| gap | string | - | Sets both gapX and gapY. Prefer spacing tokens such as calc(2 * var(--spacing-unit)). Overridden by gapX or gapY when those are set. |
| gapX | string | calc(2 * var(--spacing-unit)) | Horizontal gap for FooterActions and FooterMeta. Sets --df-options-panel-footer-gap-x. |
| gapY | string | calc(2.5 * var(--spacing-unit)) | Vertical gap between stacked footer rows such as actions and meta. Sets --df-options-panel-footer-gap-y. |
| children | ReactNode | - | Prefer OptionsPanelFooterActions for the two-up action row and OptionsPanelFooterMeta for status copy with Badge. |
| className | string | - | Additional styles on the footer region. |
Two-column action row for paired controls such as Download and Copy. Gap comes from the parent footer gapX.
| Prop | Type | Default | Notes |
|---|---|---|---|
| className | string | - | Additional styles on the actions row. |
| children | ReactNode | - | Paired action controls. |
Centered meta row for export summary copy and kit Badge. Uses text-xs muted foreground by default. Gap comes from the parent footer gapX.
| Prop | Type | Default | Notes |
|---|---|---|---|
| className | string | - | Additional styles on the meta row. |
| children | ReactNode | - | Meta copy and badges. |
Theme defaults live on :root. Override these globally or on a local host.
| Token | Type | Default | Notes |
|---|---|---|---|
| --df-options-panel-max-width | CSS length | var(--df-max-w-xs) | Maximum panel width. The shell grows with content up to this cap and the viewport. Content wraps. The body does not scroll sideways. |
| --df-options-panel-max-height | CSS length | 520px | Maximum panel height before scrolling. |
| --df-options-panel-footer-gap-x | CSS length | calc(2 * var(--spacing-unit)) | Horizontal gap for FooterActions and FooterMeta. Set by gap or gapX. |
| --df-options-panel-footer-gap-y | CSS length | calc(2.5 * var(--spacing-unit)) | Vertical gap between stacked footer rows. Set by gap or gapY. |
| --popover | CSS color | var(--df-neutral-0) | Panel fill. |
| --popover-foreground | CSS color | var(--df-neutral-900) | Panel text color. |