@k2b/ui
FloatingWindow
Movable and resizable utility content with mobile fallback.
A movable, resizable utility window that fits the viewport, uses an explicit portal scope, and becomes an inset surface on mobile.
TSX
Copyconst [open, setOpen] = createSignal(false); <div ref={appShell}> <Button variant="secondary" onClick={() => setOpen(true)}>Open inspector</Button> <Show when={open()}> <FloatingWindow title="Inspector" icon="ti ti-adjustments" initialWidth={520} initialHeight={380} resolveScope={() => appShell} onClose={() => setOpen(false)} > … </FloatingWindow> </Show></div>FloatingWindow is a movable, resizable, non-modal utility surface. It stays visible while the user continues working in the application.
Use FloatingWindow
Use it for long-lived reference work such as Help, an assistant, or a preview that belongs beside the main task.
Use a dialog when the user must finish or dismiss the task before continuing. Use an AppWorkspace.Detail when the content belongs to the current selection.
Import
import {
fitFloatingWindowRect,
FloatingWindow,
openFloatingWindow,
type FloatingWindowClose,
type FloatingWindowRect,
} from "@k2b/ui";Open and close
openFloatingWindow(view, options) mounts the window in a portal and returns an idempotent close function.
The view also receives that close function. Use it for actions inside the window.
Options include title, icon, accent, initial width and height, minimum width and height, and an optional class.
The helper restores focus to the previously focused element when the window closes.
Use FloatingWindow directly when reactive JSX already owns whether the
window is mounted. Pass resolveScope={() => owner} when the portal must stay
inside a specific .k2b-ui application shell. openFloatingWindow creates
its own styled owner and supplies that scope unless an explicit resolver is
provided.
Window behavior
Desktop windows can be moved and resized. Geometry is clamped to the viewport. Clicking a window brings it in front of other floating windows.
Below 640 pixels, the window becomes an inset surface with a 0.5rem viewport
gap and disables movement and resizing.
The component does not persist its position. Add product-owned persistence only when restoring utility geometry is a real requirement.
fitFloatingWindowRect(rect, minWidth, minHeight, viewport) is the pure
geometry helper used by the component. It returns a clamped
FloatingWindowRect; it does not read the browser or store the result.
Accessibility
The window uses role="dialog" with aria-modal="false". The title names it.
The title control supports arrow-key movement. The lower-right resize control supports arrow-key resizing. Holding Shift uses larger steps.
Escape closes only the top floating window.
Runtime
The geometry helper is SSR-safe. Portal mounting and window interaction are
browser-only. openFloatingWindow throws when document is unavailable.
Open them from an island or another hydrated client component. Do not call the helper during SSR.
Example
const close = openFloatingWindow(
(closeWindow: FloatingWindowClose) => (
<HelpReader articleId={articleId} onClose={closeWindow} />
),
{
title: "Help",
icon: "ti ti-help",
accent: app.appearance.accent,
initialWidth: 520,
initialHeight: 640,
},
);For reactive ownership:
let appShell: HTMLDivElement | undefined;
<div ref={appShell}>
<Show when={open()}>
<FloatingWindow
title="Preview"
resolveScope={() => appShell}
onClose={() => setOpen(false)}
>
<Preview />
</FloatingWindow>
</Show>
</div>;