OverlaysReact
A window inside the page: a non-modal dialog the person moves by its title bar, resizes from its edges and corners, maximizes, minimizes and closes, while the page behind stays usable.
Rendered by React 19.3.1
Loading React…Installation
npm i @hamur/reactNot on npm yet. The package goes out under the chosen name.
Anatomy
Import the component and assemble its parts:
<FloatingPanel.Root>
<FloatingPanel.Trigger />
<FloatingPanel.Portal>
<FloatingPanel.Popup>
<FloatingPanel.DragHandle>
<FloatingPanel.Title />
<FloatingPanel.Minimize />
<FloatingPanel.Maximize />
<FloatingPanel.Close />
</FloatingPanel.DragHandle>
<FloatingPanel.Body>
<FloatingPanel.Description />
</FloatingPanel.Body>
<FloatingPanel.ResizeHandle />
</FloatingPanel.Popup>
</FloatingPanel.Portal>
</FloatingPanel.Root>Examples
Move
position and size are controlled; buttons in the Body move the panel to a corner or by 10 pixels, without a drag.
API reference
40 props across 12 parts, in React syntax. className, style, render are listed once, under Shared props.
BodyFloatingPanel.Body
The panel's content, hidden while the panel is minimized. Renders a <div>.
CloseFloatingPanel.Close
A button that closes the floating panel. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
DescriptionFloatingPanel.Description
A paragraph with additional information about the floating panel. Renders a <p>.
DragHandleFloatingPanel.DragHandle
The area that moves the panel with a pointer, usually its title bar. A double-click on it toggles the maximized stage. Renders a <div>.
| Data attribute | Description |
|---|---|
data-dragging | Present while the panel is being dragged. |
data-disabled | Present when draggable is false or the panel is maximized. |
MaximizeFloatingPanel.Maximize
A toggle button for the maximized stage, pressed while the panel is maximized. Named by its text, or by labels.maximize ("Maximize") when it has none; the name never changes with the stage. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-pressed | Present while the panel is maximized. |
data-disabled | Present when the button is disabled. |
MinimizeFloatingPanel.Minimize
A toggle button for the minimized stage, pressed while the panel is minimized. Named by its text, or by labels.minimize ("Minimize") when it has none; the name never changes with the stage. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
| Data attribute | Description |
|---|---|
data-pressed | Present while the panel is minimized. |
data-disabled | Present when the button is disabled. |
PopupFloatingPanel.Popup
The panel: a non-modal dialog placed and sized in its boundary. It takes focus when it opens, and the arrow keys move it (Alt and arrows resize it) while it is focused itself. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
initialFocusDetermines the element to focus when the floating panel is opened. | boolean | React.RefObject<HTMLElement | null> | ((openType: InteractionType) => boolean | void | HTMLElement | null) | — |
finalFocusDetermines the element to focus when the floating panel is closed. | boolean | React.RefObject<HTMLElement | null> | ((closeType: InteractionType) => boolean | void | HTMLElement | null) | — |
| Data attribute | Description |
|---|---|
data-open | Present when the floating panel is open. |
data-closed | Present when the floating panel is closed. |
data-starting-style | Present when the floating panel begins animating in. |
data-ending-style | Present when the floating panel is animating out. |
data-nested | Present when the floating panel is nested within a dialog. |
data-nested-dialog-open | Present when the floating panel has open dialogs nested within it. |
data-dragging | Present while the panel is being dragged. |
data-resizing | Present while the panel is being resized. |
data-maximized | Present while the panel is maximized. |
data-minimized | Present while the panel is minimized. |
data-topmost | Present when the panel is in front of every other open panel in the document. |
| CSS variable | Description |
|---|---|
--panel-x | The rendered left edge in px from the boundary. |
--panel-y | The rendered top edge in px from the boundary. |
--panel-width | The rendered width in px, once measured or set. |
--panel-height | The rendered height in px, once set; absent while minimized. |
--panel-index | The panel's place in the document's stack of open panels, 0 at the back. |
PortalFloatingPanel.Portal
A portal element that moves the popup to a different part of the DOM. By default, the portal element is appended to <body>. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
keepMountedWhether to keep the portal mounted in the DOM while the popup is hidden. | boolean | false |
containerA parent element to render the portal element into. | HTMLElement | ShadowRoot | React.RefObject<HTMLElement | ShadowRoot | null> | null | — |
ResizeHandleFloatingPanel.ResizeHandle
An edge or a corner that resizes the panel with a pointer, the opposite side anchored. Hidden from assistive technology: Alt with the arrow keys resizes the focused Popup. Renders a <div>.
| Prop | Type | Default |
|---|---|---|
positionrequiredThe edge or corner the handle resizes from, physical. | FloatingPanelResizeHandlePosition | — |
| Data attribute | Description |
|---|---|
data-position | The edge or corner the handle resizes from. |
data-resizing | Present while this handle is resizing the panel. |
data-disabled | Present when resizable is false or the stage is not normal. |
RootFloatingPanel.Root
Groups all parts of the floating panel: a non-modal dialog the user moves, resizes, maximizes and minimizes. Doesn’t render its own element.
| Prop | Type | Default |
|---|---|---|
openWhether the floating panel is currently open. | boolean | — |
defaultOpenWhether the floating panel is initially open. | boolean | false |
onOpenChangeEvent handler called when the floating panel is opened or closed. | ((open: boolean, eventDetails: FloatingPanelRootChangeEventDetails) => void) | — |
onOpenChangeCompleteEvent handler called after any animations complete when the floating panel is opened or | ((open: boolean) => void) | — |
actionsRefA ref to imperative actions. | React.RefObject<DialogRootActions | null> | — |
handleA handle to associate the floating panel with a trigger. | FloatingPanelHandle<Payload> | — |
childrenThe content of the floating panel. | React.ReactNode | PayloadChildRenderFunction<Payload> | — |
triggerIdID of the trigger that the floating panel is associated with. | string | null | — |
defaultTriggerIdID of the trigger that the floating panel is associated with. | string | null | — |
positionThe top-left corner of the normal stage in CSS px from the boundary's top-left corner. | FloatingPanelPosition | — |
defaultPositionThe initial position, when uncontrolled. | FloatingPanelPosition | — |
onPositionChangeEvent handler called when the position changes. | ((position: FloatingPanelPosition, eventDetails: FloatingPanelRootGeometryChangeEventDetails) => void) | — |
onPositionCommittedEvent handler called when a drag or a key that moved the panel ends. | ((position: FloatingPanelPosition, eventDetails: FloatingPanelRootGeometryChangeEventDetails) => void) | — |
sizeThe border box of the normal stage in CSS px. | FloatingPanelSize | — |
defaultSizeThe initial size, when uncontrolled. | FloatingPanelSize | — |
onSizeChangeEvent handler called when the size changes. | ((size: FloatingPanelSize, eventDetails: FloatingPanelRootGeometryChangeEventDetails) => void) | — |
onSizeCommittedEvent handler called when a resize or a key that resized the panel ends. | ((size: FloatingPanelSize, eventDetails: FloatingPanelRootGeometryChangeEventDetails) => void) | — |
stageThe stage: normal, maximized (the boundary's whole box) or minimized (the Body hidden). | FloatingPanelStage | — |
defaultStageThe initial stage, when uncontrolled. | FloatingPanelStage | 'normal' |
onStageChangeEvent handler called when the stage changes. | ((stage: FloatingPanelStage, eventDetails: FloatingPanelRootStageChangeEventDetails) => void) | — |
minSizeThe smallest size a resize reaches, in CSS px. | FloatingPanelSize | { width: 120, height: 100 } |
maxSizeThe largest size a resize reaches, in CSS px. | FloatingPanelSize | — |
boundaryThe box the panel stays inside: the viewport, or an element that is the Popup's containing | Element | React.RefObject<Element | null> | "viewport" | 'viewport' |
draggableWhether the panel moves with the DragHandle and the arrow keys. | boolean | true |
resizableWhether the panel resizes with the ResizeHandles and Alt with the arrow keys. | boolean | true |
stepCSS px per arrow key. | number | 1 |
largeStepCSS px per arrow key with Shift. | number | 10 |
labelsThe toggles' aria-labels when their content has no text, over the English defaults. | Partial<FloatingPanelLabels> | — |
TitleFloatingPanel.Title
A heading that labels the floating panel. Renders a <h2>.
TriggerFloatingPanel.Trigger
A button that opens the floating panel. Renders a <button>.
| Prop | Type | Default |
|---|---|---|
handleA handle to associate the trigger with a floating panel. | FloatingPanelHandle<Payload> | — |
payloadA payload to pass to the floating panel when it is opened. | Payload | — |
idID of the trigger. | string | — |
nativeButtonWhether the component renders a native <button> element when replacing it | boolean | true |
Shared props
Every part that renders an element takes these.
| Prop | Type |
|---|---|
classNameCSS class applied to the element, or a function that
returns a class based on the component's state. | string | ((state: State) => string | undefined) |
styleStyle applied to the element, or a function that
returns a style object based on the component's state. | React.CSSProperties | ((state: State) => React.CSSProperties | undefined) |
renderAllows you to replace the component's HTML element
with a different tag, or compose it with another component. | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, State> |
Keyboard and accessibility
Keyboard
| Keys | Does | Part |
|---|---|---|
| ArrowLeftArrowRightArrowUpArrowDown | Moves the focused Popup by step (1 pixel), physically | Popup |
| Shift | Moves it by largeStep (10 pixels) | Popup |
| Alt | Resizes it by step, the top-left corner anchored | Popup |
| AltShift | Resizes it by largeStep | Popup |
| Escape | Closes the panel, from anywhere inside it | Popup |
| Escape | During a drag or a resize, puts the panel back where it was and keeps it open | DragHandle |
| Tab | From the Trigger, moves to the Popup; from its last control, leaves the panel | Popup |
| ShiftTab | From the first control in the Popup, returns to the Popup | Popup |
| EnterSpace | Presses Maximize, Minimize or Close | Maximize |
Accessibility
| Part | Sets | So that |
|---|---|---|
| Trigger | aria-haspopup="dialog", aria-expanded, aria-controls | a screen reader says the button opens a dialog and whether it is open |
| Popup | role="dialog", no aria-modal, aria-labelledby to Title and aria-describedby to Description | the panel is announced as a dialog with its heading, and the page behind stays reachable |
| Popup | tabindex="0" while it can move or resize, -1 otherwise | the move and resize keys are reachable with Shift+Tab from the first control |
| Maximize | a button with aria-pressed, named "Maximize" (labels) when its content has no text | the toggle says its state, and its name does not change with the stage |
| Minimize | a button with aria-pressed, named "Minimize" (labels) when its content has no text | the same for the minimized stage |
| Body | hidden while minimized | a minimized panel's content is neither shown nor read |
| DragHandle | no role and no focus, touch-action: none and user-select: none | a drag neither scrolls the page nor selects text; the keys on the Popup do the same work |
| ResizeHandle | aria-hidden="true", no focus | a handle is not read; Alt with the arrow keys resizes the focused Popup |
Focus order
- Opening moves focus to the Popup itself, so the arrow keys move it at once, or to
initialFocus. - Focus is not trapped:
Tabgoes from the Trigger to the Popup and from its last control to the element after the Trigger. - A press on the DragHandle focuses the Popup only when focus is outside it; minimizing moves focus out of the hidden Body to the Popup.
- Closing returns focus to the Trigger, or to
finalFocus.