Marble UI
← Components

Floating Panel / Window

Preview: Default

Non-modal, draggable and resizable panel (inspector, chat, notes). Drag the title bar or focus it and use arrow keys (Shift = 32px); drag the corner grip or use Alt+arrow keys on the title bar to resize. Both stay inside the parent when `bounds="parent"`. Minimize collapses to the title bar.

Import

import { FloatingPanel } from '@marbleui/ui'

Props

<FloatingPanel>

PropTypeDefaultDescription
children *requiredReactNode—
title *requiredstring—
bounds'none' | 'parent''parent'
classNamestring—
defaultPosition{ x: number; y: number; }{ x: 24, y: 24 }
defaultSize{ w: number; h: number; }{ w: 320, h: 260 }
onClose() => void—

Usage notes

Non-modal panel over a canvas or workspace that can be dragged, resized and minimized: an inspector, notes, a chat. For a panel anchored to a trigger use Popover; for an edge panel use Sheet.

<div className="relative h-[32rem]">
  <ArtboardView />
  {open && <FloatingPanel title="Inspector" onClose={() => setOpen(false)}>…</FloatingPanel>}
</div>
  • It’s absolutely positioned, so put it in a relative container. With bounds="parent" (default) dragging stays inside that parent; "none" removes the limit.
  • defaultPosition and defaultSize (minimum 220×140) are starting values only. Position and size aren’t reported back and reset on remount. You decide when it renders; onClose adds a close button.
  • Keyboard: Tab to the title bar and use the arrow keys (8 px, 32 px with Shift). Alt+arrow keys resize it, like the bottom-right grip does with a pointer; with bounds="parent" both moving and resizing stay inside the parent.
  • It’s a role="dialog" named by title, but opening doesn’t move focus into it and Escape doesn’t close it.
Example source · show
import { useState } from 'react'
import { Button } from '../button'
import { FloatingPanel } from './floating-panel'

function Demo() {
  const [open, setOpen] = useState(true)
  return (
    <div className="relative h-80 w-[34rem] overflow-hidden rounded-xl border border-border bg-[radial-gradient(var(--border)_1px,transparent_1px)] [background-size:16px_16px]">
      {!open && <Button className="m-4" variant="outline" onPress={() => setOpen(true)}>Open inspector</Button>}
      {open && <FloatingPanel title="Inspector" onClose={() => setOpen(false)}><dl className="grid grid-cols-2 gap-y-2 text-sm"><dt className="text-fg-muted">Width</dt><dd className="text-fg">320px</dd><dt className="text-fg-muted">Radius</dt><dd className="text-fg">12px</dd><dt className="text-fg-muted">Fill</dt><dd className="text-fg">surface</dd></dl><p className="mt-3 text-xs text-fg-muted">Drag the title bar, resize from the corner.</p></FloatingPanel>}
    </div>
  )
}
export const styles = { Default: Demo }

Source: packages/ui/src/floating-panel/floating-panel.tsx

Catalog

Category
overlays
Priority
Extra
Primitive
custom
React Aria
useMove