Window
Window is a thin wrapper over Ark UI floating-panel that displays content in a non-modal floating window. It is draggable and resizable, with a header (drag handle), a body, and stage controls (minimize/maximize/restore). The portal, positioner, and content are all swallowed by Window — its children are the window parts directly, no WindowContent wrapper. Content uses the existing --popover tokens. Open state is controlled from outside: pass open/onOpenChange (or use defaultOpen) and open it with any button.
Window
// apps/docs/src/components/examples/window/default/react.tsx
import { useState } from 'react';
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-react';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-react';
export default function ReactWindowDefault() {
const [open, setOpen] = useState(false);
return (
<div className="flex flex-col items-center gap-4">
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
>
Open Window
</button>
<Window open={open} onOpenChange={(e) => setOpen(e.open)}>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Window</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>Drag me around by the header and resize me from the corner.</WindowBody>
<WindowResizeTrigger />
</Window>
</div>
);
}<!-- apps/docs/src/components/examples/window/default/svelte.svelte -->
<script lang="ts">
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-svelte';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-svelte';
let open = $state(false);
</script>
<div class="flex flex-col items-center gap-4">
<button
type="button"
class="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
onclick={() => (open = true)}
>
Open Window
</button>
<Window bind:open>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Window</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>Drag me around by the header and resize me from the corner.</WindowBody>
<WindowResizeTrigger />
</Window>
</div>Examples
Anchor Position
Anchored Position
// apps/docs/src/components/examples/window/anchor-position/react.tsx
import { useRef, useState } from 'react';
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-react';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-react';
export default function ReactWindowAnchorPosition() {
const [open, setOpen] = useState(false);
const buttonRef = useRef<HTMLButtonElement>(null);
return (
<div className="flex flex-col items-center gap-4">
<button
ref={buttonRef}
type="button"
onClick={() => setOpen(true)}
className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
>
Open Window
</button>
<Window
open={open}
onOpenChange={(e) => setOpen(e.open)}
getAnchorPosition={() => {
const r = buttonRef.current?.getBoundingClientRect();
return { x: r?.x ?? 0, y: (r?.y ?? 0) + (r?.height ?? 0) + 8 };
}}
>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Anchored Position</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>This window opens just below the button.</WindowBody>
<WindowResizeTrigger />
</Window>
</div>
);
}<!-- apps/docs/src/components/examples/window/anchor-position/svelte.svelte -->
<script lang="ts">
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-svelte';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-svelte';
let open = $state(false);
let buttonEl: HTMLButtonElement | undefined = $state();
</script>
<div class="flex flex-col items-center gap-4">
<button
bind:this={buttonEl}
type="button"
class="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
onclick={() => (open = true)}
>
Open Window
</button>
<Window
bind:open
getAnchorPosition={() => {
const r = buttonEl?.getBoundingClientRect();
return { x: r?.x ?? 0, y: (r?.y ?? 0) + (r?.height ?? 0) + 8 };
}}
>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Anchored Position</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>This window opens just below the button.</WindowBody>
<WindowResizeTrigger />
</Window>
</div>Context
Context
Window is closed
The status above is read via useWindowContext.
// apps/docs/src/components/examples/window/context/react.tsx
import { useState } from 'react';
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
useWindowContext,
} from '@cloudvoyant/helical-react';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-react';
function WindowStatus() {
const window = useWindowContext();
return <p className="text-sm text-muted-foreground">Window is {window.open ? 'open' : 'closed'}</p>;
}
export default function ReactWindowContext() {
const [open, setOpen] = useState(false);
return (
<div className="flex flex-col items-center gap-4">
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
>
Open Window
</button>
<Window open={open} onOpenChange={(e) => setOpen(e.open)}>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Context</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>
<WindowStatus />
<p className="text-sm text-muted-foreground">The status above is read via useWindowContext.</p>
</WindowBody>
<WindowResizeTrigger />
</Window>
</div>
);
}<!-- apps/docs/src/components/examples/window/context/svelte.svelte -->
<script lang="ts">
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
WindowContext,
} from '@cloudvoyant/helical-svelte';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-svelte';
let open = $state(false);
</script>
<div class="flex flex-col items-center gap-4">
<button
type="button"
class="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
onclick={() => (open = true)}
>
Open Window
</button>
<Window bind:open>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Context</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>
<WindowContext>
{#snippet render(window)}
<p class="text-sm text-muted-foreground">Window is {window().open ? 'open' : 'closed'}</p>
{/snippet}
</WindowContext>
<p class="text-sm text-muted-foreground">The status above is read via the window context.</p>
</WindowBody>
<WindowResizeTrigger />
</Window>
</div>Controlled Position
Controlled Position
// apps/docs/src/components/examples/window/controlled-position/react.tsx
import { useState } from 'react';
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-react';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-react';
export default function ReactWindowControlledPosition() {
const [open, setOpen] = useState(false);
const [position, setPosition] = useState({ x: 120, y: 60 });
return (
<div className="flex flex-col items-center gap-4">
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
>
Open Window
</button>
<Window
open={open}
onOpenChange={(e) => setOpen(e.open)}
position={position}
onPositionChange={(e) => setPosition(e.position)}
>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Controlled Position</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>
Position: ({position.x}, {position.y})
</WindowBody>
<WindowResizeTrigger />
</Window>
</div>
);
}<!-- apps/docs/src/components/examples/window/controlled-position/svelte.svelte -->
<script lang="ts">
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-svelte';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-svelte';
let open = $state(false);
let position = $state({ x: 120, y: 60 });
</script>
<div class="flex flex-col items-center gap-4">
<button
type="button"
class="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
onclick={() => (open = true)}
>
Open Window
</button>
<Window bind:open bind:position>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Controlled Position</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>
Position: ({position.x}, {position.y})
</WindowBody>
<WindowResizeTrigger />
</Window>
</div>Controlled Size
Controlled Size
// apps/docs/src/components/examples/window/controlled-size/react.tsx
import { useState } from 'react';
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-react';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-react';
export default function ReactWindowControlledSize() {
const [open, setOpen] = useState(false);
const [size, setSize] = useState({ width: 320, height: 220 });
return (
<div className="flex flex-col items-center gap-4">
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
>
Open Window
</button>
<Window open={open} onOpenChange={(e) => setOpen(e.open)} size={size} onSizeChange={(e) => setSize(e.size)}>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Controlled Size</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>
Size: {size.width} × {size.height}
</WindowBody>
<WindowResizeTrigger />
</Window>
</div>
);
}<!-- apps/docs/src/components/examples/window/controlled-size/svelte.svelte -->
<script lang="ts">
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-svelte';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-svelte';
let open = $state(false);
let size = $state({ width: 320, height: 220 });
</script>
<div class="flex flex-col items-center gap-4">
<button
type="button"
class="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
onclick={() => (open = true)}
>
Open Window
</button>
<Window bind:open bind:size>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Controlled Size</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>
Size: {size.width} × {size.height}
</WindowBody>
<WindowResizeTrigger />
</Window>
</div>Lazy Mount
// apps/docs/src/components/examples/window/lazy-mount/react.tsx
import { useState } from 'react';
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-react';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-react';
export default function ReactWindowLazyMount() {
const [open, setOpen] = useState(false);
return (
<div className="flex flex-col items-center gap-4">
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
>
Open Window
</button>
<Window open={open} onOpenChange={(e) => setOpen(e.open)} lazyMount unmountOnExit>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Lazy Mounted</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>This window mounts only when first opened.</WindowBody>
<WindowResizeTrigger />
</Window>
</div>
);
}<!-- apps/docs/src/components/examples/window/lazy-mount/svelte.svelte -->
<script lang="ts">
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-svelte';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-svelte';
let open = $state(false);
</script>
<div class="flex flex-col items-center gap-4">
<button
type="button"
class="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
onclick={() => (open = true)}
>
Open Window
</button>
<Window bind:open lazyMount unmountOnExit>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Lazy Mounted</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>This window mounts only when first opened.</WindowBody>
<WindowResizeTrigger />
</Window>
</div>Open State
Controlled Open State
// apps/docs/src/components/examples/window/open-state/react.tsx
import { useState } from 'react';
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-react';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-react';
export default function ReactWindowOpenState() {
const [open, setOpen] = useState(false);
return (
<div className="flex flex-col items-center gap-4">
<button
type="button"
onClick={() => setOpen(true)}
className="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
>
Open Window
</button>
<Window open={open} onOpenChange={(e) => setOpen(e.open)}>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Controlled Open State</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>The open state is controlled by the checkbox.</WindowBody>
<WindowResizeTrigger />
</Window>
<label className="flex items-center gap-2 text-sm text-muted-foreground">
<input type="checkbox" checked={open} onChange={(e) => setOpen(e.target.checked)} />
Open
</label>
</div>
);
}<!-- apps/docs/src/components/examples/window/open-state/svelte.svelte -->
<script lang="ts">
import {
Window,
WindowDragTrigger,
WindowHeader,
WindowTitle,
WindowControl,
WindowStageTrigger,
WindowDismiss,
WindowBody,
WindowResizeTrigger,
} from '@cloudvoyant/helical-svelte';
import { Minus, Maximize2, RotateCcw, X } from 'lucide-svelte';
let open = $state(false);
</script>
<div class="flex flex-col items-center gap-4">
<button
type="button"
class="rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground"
onclick={() => (open = true)}
>
Open Window
</button>
<Window bind:open>
<WindowDragTrigger>
<WindowHeader>
<WindowTitle>Controlled Open State</WindowTitle>
<WindowControl>
<WindowStageTrigger stage="minimized">
<Minus />
</WindowStageTrigger>
<WindowStageTrigger stage="maximized">
<Maximize2 />
</WindowStageTrigger>
<WindowStageTrigger stage="default">
<RotateCcw />
</WindowStageTrigger>
<WindowDismiss>
<X />
</WindowDismiss>
</WindowControl>
</WindowHeader>
</WindowDragTrigger>
<WindowBody>The open state is controlled by the checkbox.</WindowBody>
<WindowResizeTrigger />
</Window>
<label class="flex items-center gap-2 text-sm text-muted-foreground">
<input type="checkbox" bind:checked={open} />
Open
</label>
</div>API Reference
Window
Props
| Prop | Type | Default | Description |
|---|---|---|---|
open / onOpenChange |
boolean / (details) => void |
— | Controlled open state. |
defaultOpen |
boolean |
false |
Initial open state when uncontrolled. |
size / onSizeChange |
Size / (details) => void |
— | Controlled size. |
position / onPositionChange |
Point / (details) => void |
— | Controlled position. |
defaultSize / defaultPosition |
Size / Point |
— | Uncontrolled size/position. |
draggable / resizable |
boolean |
true |
Enable dragging/resizing. |
minSize / maxSize |
Size |
— | Size constraints. |
gridSize |
number |
1 |
Snap grid for dragging. |
getAnchorPosition |
(details) => Point |
— | Initial position relative to the opening button on open. |
lazyMount / unmountOnExit |
boolean |
false |
Mount/unmount content only around open/close. |
className / class |
string |
— | Class applied to the window content panel. |
positionerClassName / positionerClass |
string |
— | Class applied to the internal positioner. |
Accepts all Ark FloatingPanelRootProps.
WindowProvider
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value |
UseFloatingPanelReturn |
— | The API object from useWindow(). |
WindowDragTrigger
Accepts all Ark FloatingPanelDragTriggerProps plus className/class. Wrap the header in this to make it the drag handle.
WindowHeader / WindowTitle / WindowControl / WindowBody
Passthroughs over their Ark counterparts plus className/class.
WindowStageTrigger
Accepts all Ark FloatingPanelStageTriggerProps plus className/class. Set stage to minimized/maximized/default to change the panel stage.
WindowDismiss
Accepts all Ark FloatingPanelCloseTriggerProps plus className/class. Ships with aria-label="Close".
WindowResizeTrigger
Accepts all Ark FloatingPanelResizeTriggerProps plus className/class. The corner drag handle.
Hooks
useWindow— Ark machine factory; returnsUseFloatingPanelReturnforWindowProvider.useWindowContext— reads the current panel state (open, position, size, stage) from context.
Accessibility
The panel content carries role="dialog" with an accessible name from WindowTitle. Keyboard: Esc closes (when closeOnEscape is set); focus moves to the content on open and back to the trigger on close; the header drag and corner resize handles are exposed as controls.
Theme extensions
None. The panel reuses existing tokens (--popover, --popover-foreground, --border, --accent, --muted, --ring) — no theme.css changes.