The window system is the heart of the Old Windows experience. Every window is a self-contained Framer MotionDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/apursley2012/old-windows/llms.txt
Use this file to discover all available pages before exploring further.
motion.div that reads its position, size, and state from WindowContext and writes back to it through six context actions. Understanding this system is the key to customizing or extending the portfolio.
Window lifecycle
Windows are born and die insideWindowContext. The context maintains a windows array in React state. Each entry is a plain JavaScript object:
| Action | Effect |
|---|---|
openWindow(id, title, component, icon, { width, height }) | Appends a new window object. If the window already exists and is minimized, restores it instead. |
closeWindow(id) | Filters the window out of the array. Transfers focus to the highest remaining z-index window. |
minimizeWindow(id) | Sets state: 'minimized'. Clears activeWindowId. |
maximizeWindow(id) | Sets state: 'maximized'. Calls focusWindow internally. |
restoreWindow(id) | Sets state: 'open'. Calls focusWindow internally. |
focusWindow(id) | Increments the module-level z-index counter and sets it on the window. Sets activeWindowId. |
Window states
A window object’sstate field controls how the Window component renders:
'minimized'— TheWindowcomponent returnsnull. The window disappears from the desktop but its taskbar button remains. Clicking the button callsrestoreWindow.'open'— The window renders at itsdefaultX/defaultYposition with its configuredwidthandheight. It is freely draggable.'maximized'— The window fills the full viewport width andcalc(100% - 40px)height (the 40 px accounts for the taskbar). Dragging is disabled; the Framer Motiondragprop is set tofalse.
Maximized windows use an inline
style of { width: '100%', height: 'calc(100% - 40px)' } and Tailwind classes w-full h-full top-0 left-0 (via the absolute positioning of the motion.div). This means the 40 px taskbar at the bottom is always visible even when a window is maximized.Framer Motion integration
Each window is rendered as amotion.div (imported from Framer Motion v11 as the Cc.div factory). The drag lifecycle works like this:
dragListener: falsemeans Framer Motion will not start a drag from pointer events on themotion.divitself — the drag only starts when the title bar callsdragControls.start(e)viaonPointerDown.dragMomentum: falsedisables the inertia animation so windows stop precisely where you release them, consistent with native OS window behavior.initialsets the spawn position usingdefaultXanddefaultYfrom the window object, along withscale: 0.9andopacity: 0so each new window animates in smoothly.- When the window is maximized,
initial={false}is passed instead, andanimatetargets{ x: 0, y: 0, scale: 1, opacity: 1 }to fill the viewport.
Title bar interactions
The title bar handles three interactions:onPointerDown— Starts the drag viadragControls.start(event)(only when not maximized) and focuses the window.onDoubleClick— Toggles between maximize and restore: if currently maximized, callsrestoreWindow(id); otherwise callsmaximizeWindow(id).- The three window-chrome buttons call
minimizeWindow,maximizeWindow/restoreWindow, andcloseWindowrespectively, each withevent.stopPropagation()to prevent the title-baronPointerDownfrom firing as well.
bg-win-navy text-white) while inactive windows use the dark gray (bg-win-gray-dark text-win-gray-light), driven by comparing the window’s id against activeWindowId from context.
z-index management
WindowContext.js declares a module-level counter initialized to 10 outside any React component. Every call to focusWindow(id) increments this counter and sets the new value as the window’s zIndex:
openWindow also increment the counter and apply it immediately.
Default positioning (cascade)
WhenopenWindow creates a new window object it calculates the initial position based on how many windows are already open:
50 + windows.length * 30 formula cascades each new window 30 px further down and to the right than the previous one, replicating the classic Windows cascade behavior. Custom defaultX / defaultY values passed via the options argument (e.g., from a programmatic openWindow call) override this calculation.
Default window dimensions
openWindow falls back to 600 × 400 px if no dimensions are provided:
width and height values in config/apps.js, ranging from 450 × 500 (the contact guestbook) to 800 × 600 (the projects explorer).
WebRing navigation bar
Each window’s status bar at the bottom contains three buttons —< Prev, Random, and Next > — that implement a WebRing-style navigation. They use apps.findIndex to locate the current window in the apps array, then compute the adjacent or random index: