Window
The Window suite is a set of composable primitives for Windows 98 application chrome. Window is the root: it owns window state (active, maximized, minimized) and shares it with the slots below.
Window renders no DOM of its own — it is a context provider, like a shadcn/ui root. That is deliberate: window managers need full control over layout, portalling, stacking, and drag/resize, so those concerns stay in your app while Window only coordinates state. Wire the useDraggable and useResizable hooks onto the frame yourself — see Build a complete window below.
Composition
Use the following composition to build a Window:
Window
└── WindowFrame
├── WindowTitleBar
│ ├── WindowTitle
│ └── WindowButtons
│ ├── WindowMinimizeButton
│ ├── WindowMaximizeButton
│ └── WindowCloseButton
├── WindowMenuBar
│ └── WindowMenuBarMenu
│ ├── WindowMenuBarTrigger
│ └── WindowMenuBarContent
│ ├── MenuItem
│ ├── MenuCheckboxItem
│ ├── MenuRadioGroup
│ │ └── MenuRadioItem
│ ├── MenuSeparator
│ └── MenuSub
│ ├── MenuSubTrigger
│ └── MenuSubContent
├── WindowContent
├── WindowStatusBar
│ └── WindowStatusBarField
└── WindowResizeGripProps
Window
The root context provider. Renders no element of its own.
| Prop | Type | Default | Description |
|---|---|---|---|
active | boolean | true | Active (focused) styling for descendant slots. |
minimized | boolean | false | Hides the frame via CSS while preserving the DOM. |
defaultMaximized | boolean | false | Initial maximized state. |
positioning | 'absolute' | 'fixed' | 'fixed' | Positioning mode inherited by the frame and overlay. |
maximizable | boolean | true | Whether the maximize control is enabled. |
Slots
| Export | Key props | Description |
|---|---|---|
WindowFrame | div props | Outer raised frame. |
WindowTitleBar, WindowTitle, WindowButtons | div props | Title bar slots. |
WindowMinimizeButton, WindowMaximizeButton, WindowCloseButton, WindowHelpButton | button props | Title bar controls. |
WindowMenuBar, WindowMenuBarMenu, WindowMenuBarTrigger, WindowMenuBarContent, WindowMenuBarItem | value, defaultValue, onValueChange, menu/menuitem props | Top-level window menu bar with dropdown coordination. |
WindowContent | div props | Scroll/content body. |
WindowOverlay | positioning, div props | Semi-transparent backdrop (aria-hidden). Inherits positioning from context. |
WindowPortal | container | Portal that respects the scoped LayerProvider target. Falls back to document.body. |
WindowResizeGrip | div props | Bottom-right resize handle rendered with Marlett font glyphs. Hidden when maximized. |
WindowStatusBar, WindowStatusBarField | div props | Bottom status area. |
useWindowContext | - | Read state / actions / meta inside custom chrome. |
Examples
Primitives assembly
import { Button, Window, WindowButtons, WindowCloseButton, WindowContent, WindowFrame, WindowMaximizeButton, WindowMenuBar, WindowMenuBarItem, WindowMinimizeButton, WindowStatusBar, WindowStatusBarField, WindowTitle, WindowTitleBar } from '@murasaki-io/react98'
export function WindowBasicDemo(): React.ReactElement {
return (
<div className="relative h-56 w-88">
<Window positioning="absolute" maximizable={false}>
<WindowFrame className="inset-0 min-h-0">
<WindowTitleBar>
<WindowTitle>Example.exe</WindowTitle>
<WindowButtons>
<WindowMinimizeButton />
<WindowMaximizeButton disabled />
<WindowCloseButton />
</WindowButtons>
</WindowTitleBar>
<WindowMenuBar>
<WindowMenuBarItem>File</WindowMenuBarItem>
<WindowMenuBarItem>Edit</WindowMenuBarItem>
<WindowMenuBarItem>Help</WindowMenuBarItem>
</WindowMenuBar>
<WindowContent className="p-2">
<p className="m-0 mb-2">Window primitives compose into app chrome.</p>
<Button>Close</Button>
</WindowContent>
<WindowStatusBar>
<WindowStatusBarField>Ready</WindowStatusBarField>
<WindowStatusBarField grow={false}>NUM</WindowStatusBarField>
</WindowStatusBar>
</WindowFrame>
</Window>
</div>
)
}Menu bar dropdowns
import {
Button,
MenuItem,
MenuSeparator,
Window,
WindowButtons,
WindowCloseButton,
WindowContent,
WindowFrame,
WindowMaximizeButton,
WindowMenuBar,
WindowMenuBarContent,
WindowMenuBarMenu,
WindowMenuBarTrigger,
WindowMinimizeButton,
WindowStatusBar,
WindowStatusBarField,
WindowTitle,
WindowTitleBar,
} from '@murasaki-io/react98'
import { useState } from 'react'
export function WindowMenuDemo(): React.ReactElement {
const [status, setStatus] = useState('Ready')
return (
<div className="relative h-64 w-[384px]">
<Window positioning="absolute" maximizable={false}>
<WindowFrame className="inset-0 min-h-0">
<WindowTitleBar>
<WindowTitle>Write.exe</WindowTitle>
<WindowButtons>
<WindowMinimizeButton />
<WindowMaximizeButton disabled />
<WindowCloseButton />
</WindowButtons>
</WindowTitleBar>
<WindowMenuBar>
<WindowMenuBarMenu value="file">
<WindowMenuBarTrigger>File</WindowMenuBarTrigger>
<WindowMenuBarContent className="w-40">
<MenuItem reserveIconSpace onClick={() => setStatus('New document')}>New</MenuItem>
<MenuItem reserveIconSpace onClick={() => setStatus('Open file')}>Open…</MenuItem>
<MenuItem reserveIconSpace disabled>Save</MenuItem>
<MenuSeparator />
<MenuItem reserveIconSpace onClick={() => setStatus('Print')}>Print…</MenuItem>
<MenuSeparator />
<MenuItem reserveIconSpace disabled>Exit</MenuItem>
</WindowMenuBarContent>
</WindowMenuBarMenu>
<WindowMenuBarMenu value="edit">
<WindowMenuBarTrigger>Edit</WindowMenuBarTrigger>
<WindowMenuBarContent className="w-36">
<MenuItem reserveIconSpace disabled>Undo</MenuItem>
<MenuSeparator />
<MenuItem reserveIconSpace onClick={() => setStatus('Cut')}>Cut</MenuItem>
<MenuItem reserveIconSpace onClick={() => setStatus('Copy')}>Copy</MenuItem>
<MenuItem reserveIconSpace disabled>Paste</MenuItem>
</WindowMenuBarContent>
</WindowMenuBarMenu>
<WindowMenuBarMenu value="help">
<WindowMenuBarTrigger>Help</WindowMenuBarTrigger>
<WindowMenuBarContent className="w-44">
<MenuItem reserveIconSpace onClick={() => setStatus('Help contents')}>Contents</MenuItem>
<MenuSeparator />
<MenuItem reserveIconSpace onClick={() => setStatus('About Write')}>About Write</MenuItem>
</WindowMenuBarContent>
</WindowMenuBarMenu>
</WindowMenuBar>
<WindowContent className="flex flex-col gap-2 p-2">
<p className="m-0">A tiny document window with classic top menus.</p>
<Button onClick={() => setStatus('Button clicked')}>Close</Button>
</WindowContent>
<WindowStatusBar>
<WindowStatusBarField>{status}</WindowStatusBarField>
</WindowStatusBar>
</WindowFrame>
</Window>
</div>
)
}Build a complete window
Because Window renders no DOM, a draggable, resizable window is assembled by wiring the interaction hooks onto the frame and title bar yourself:
import {
MenuItem,
useDraggable,
useResizable,
Window,
WindowButtons,
WindowCloseButton,
WindowContent,
WindowFrame,
WindowMaximizeButton,
WindowMenuBar,
WindowMenuBarContent,
WindowMenuBarMenu,
WindowMenuBarTrigger,
WindowMinimizeButton,
WindowResizeGrip,
WindowStatusBar,
WindowStatusBarField,
WindowTitle,
WindowTitleBar,
} from '@murasaki-io/react98'
function MyWindow() {
const { setTargetRef: setDragTarget, setDragRef } = useDraggable<HTMLDivElement, HTMLDivElement>()
const { setTargetRef: setResizeTarget, setResizeRef } = useResizable<HTMLDivElement, HTMLDivElement>({
minWidth: 320,
minHeight: 200,
})
const setFrameRef = (el: HTMLDivElement | null) => {
setDragTarget(el)
setResizeTarget(el)
}
return (
<Window positioning="absolute">
<WindowFrame ref={setFrameRef} style={{ left: 80, top: 60, width: 480, height: 320 }}>
<WindowTitleBar ref={setDragRef}>
<WindowTitle>Notepad 98</WindowTitle>
<WindowButtons>
<WindowMinimizeButton />
<WindowMaximizeButton />
<WindowCloseButton />
</WindowButtons>
</WindowTitleBar>
<WindowMenuBar>
<WindowMenuBarMenu value="file">
<WindowMenuBarTrigger>File</WindowMenuBarTrigger>
<WindowMenuBarContent>
<MenuItem reserveIconSpace>New</MenuItem>
<MenuItem reserveIconSpace>Open…</MenuItem>
</WindowMenuBarContent>
</WindowMenuBarMenu>
</WindowMenuBar>
<WindowContent className="p-2">Your app content</WindowContent>
<WindowStatusBar>
<WindowStatusBarField>Ready</WindowStatusBarField>
</WindowStatusBar>
<WindowResizeGrip ref={setResizeRef} />
</WindowFrame>
</Window>
)
}For a full desktop shell — multiple windows, z-index stacking, taskbar, and position persistence — see the Build a desktop guide.
Positioning model
left/topandwidth/heightare static inline styles you set once for the initial layout.- Dragging adds a
translate()transform on top of the base position;useDraggablere-clamps it to the container/viewport. - Resizing overwrites the inline
width/height;useResizableclamps it tominWidth/minHeight/maxWidth/maxHeightand the container/viewport. useDraggableignores mousedowns onbutton/a/input/select/textarea/[role="button"], so the title-bar buttons keep working.
ARIA
Window primitives provide visual structure. Add dialog/window roles, labels, focus management, and close behavior at the app shell layer.
Keyboard
Window chrome primitives do not add global keyboard shortcuts. Title-bar buttons and menu-bar items keep their native button or menu keyboard behavior.
SSR
Window owns maximized state. Use defaultMaximized for initial layout and keep positioning classes stable across hydration.