Table of Contents
- Introduction
- Design Philosophy and Architecture
- Development Process
- Core Components Implementation
- Creating an NPM Package
- Documentation Standards
- Testing Strategy
- Publishing and Distribution
- Best Practices and Lessons Learned
Introduction
This article documents the creation of web-window-manager
, a comprehensive JavaScript library that brings desktop-like window management capabilities to web applications. We'll explore the architectural decisions, implementation details, and the process of packaging and distributing the library through NPM.
Project Goals
- Create a lightweight, dependency-free window management system
- Provide an intuitive API that mirrors desktop OS conventions
- Ensure accessibility and performance across modern browsers
- Build a foundation that developers can extend for their specific needs
Design Philosophy and Architecture
Core Design Principles
- Progressive Enhancement: The library should work without any build tools or frameworks
- Composability: Each component should function independently while integrating seamlessly
- Performance First: Minimize reflows and repaints through efficient DOM manipulation
- Accessibility: Ensure keyboard navigation and screen reader compatibility
- Extensibility: Provide hooks and events for custom functionality
Architectural Decisions
Why Vanilla JavaScript?
We chose vanilla JavaScript over a framework-specific implementation for several reasons:
- Universal Compatibility: Works with any framework or no framework at all
- Minimal Overhead: No virtual DOM or framework abstractions
- Learning Curve: Developers familiar with DOM APIs can immediately understand the codebase
- Performance: Direct DOM manipulation when done correctly can outperform framework abstractions
Component Architecture
WindowManager
├── Window System
│ ├── Window Creation
│ ├── Drag & Drop Handler
│ ├── Resize Handler
│ └── Focus Management
├── Context Menu System
│ ├── Menu Renderer
│ └── Position Calculator
├── Modal System
│ ├── Overlay Manager
│ └── Modal Renderer
└── Notification System
├── Queue Manager
└── Animation Controller
Development Process
Step 1: Requirements Gathering
We identified key features by analyzing popular desktop environments:
- Windows: Title bar, control buttons, resize handles
- macOS: Traffic light controls, smooth animations
- Context Menus: Right-click functionality with nested menus
- Modals: Focus trapping and backdrop interaction
- Notifications: Toast-style alerts with auto-dismiss
Step 2: API Design
We followed a builder pattern approach for flexibility:
// Simple usage
wm.createWindow({ title: 'My App' });
// Advanced usage
wm.createWindow({
title: 'Advanced Window',
width: 600,
height: 400,
position: { x: 100, y: 100 },
resizable: true,
onClose: () => saveState(),
content: document.getElementById('app-content')
});
Step 3: Implementation Strategy
- Core Window Class: Established the base window functionality
- Event System: Implemented a pub/sub pattern for window events
- Interaction Handlers: Added drag, resize, and focus management
- Visual Components: Created context menus, modals, and notifications
- Style System: Developed a themeable CSS architecture
Core Components Implementation
Window Management System
The window system uses a Map to track windows and their states:
class WindowManager {
constructor(options = {}) {
this.windows = new Map();
this.config = { ...DEFAULT_CONFIG, ...options };
this.zIndexManager = new ZIndexManager();
this.eventBus = new EventEmitter();
this.init();
}
createWindow(options) {
const window = new Window(this, options);
this.windows.set(window.id, window);
this.eventBus.emit('window:created', window);
return window;
}
}
Drag and Drop Implementation
We implemented drag handling using pointer events for better touch support:
class DragHandler {
constructor(window, handle) {
this.window = window;
this.handle = handle;
this.isDragging = false;
this.dragStart = { x: 0, y: 0 };
this.windowStart = { x: 0, y: 0 };
this.bindEvents();
}
bindEvents() {
this.handle.addEventListener('pointerdown', this.onPointerDown);
document.addEventListener('pointermove', this.onPointerMove);
document.addEventListener('pointerup', this.onPointerUp);
}
onPointerDown = (e) => {
this.isDragging = true;
this.dragStart = { x: e.clientX, y: e.clientY };
this.windowStart = {
x: this.window.position.x,
y: this.window.position.y
};
this.handle.setPointerCapture(e.pointerId);
}
}
Context Menu System
The context menu system calculates optimal positioning:
class ContextMenu {
constructor(items, options = {}) {
this.items = items;
this.options = options;
this.element = this.render();
this.positionMenu();
}
positionMenu() {
const { x, y } = this.options.position;
const menuRect = this.element.getBoundingClientRect();
const viewport = {
width: window.innerWidth,
height: window.innerHeight
};
// Adjust position to keep menu in viewport
const adjustedX = Math.min(x, viewport.width - menuRect.width);
const adjustedY = Math.min(y, viewport.height - menuRect.height);
this.element.style.left = `${Math.max(0, adjustedX)}px`;
this.element.style.top = `${Math.max(0, adjustedY)}px`;
}
}
Creating an NPM Package
Project Structure
web-window-manager/
├── src/
│ ├── core/
│ │ ├── WindowManager.js
│ │ ├── Window.js
│ │ └── EventEmitter.js
│ ├── components/
│ │ ├── ContextMenu.js
│ │ ├── Modal.js
│ │ └── Notification.js
│ ├── utils/
│ │ ├── dom.js
│ │ └── position.js
│ ├── styles/
│ │ ├── base.css
│ │ └── themes/
│ └── index.js
├── dist/
│ ├── web-window-manager.js
│ ├── web-window-manager.min.js
│ └── web-window-manager.css
├── docs/
│ ├── api/
│ ├── examples/
│ └── guides/
├── test/
├── package.json
├── README.md
├── LICENSE
└── rollup.config.js
Package.json Configuration
{
"name": "web-window-manager",
"version": "1.0.0",
"description": "A modern window management library for web applications",
"main": "dist/web-window-manager.js",
"module": "src/index.js",
"style": "dist/web-window-manager.css",
"files": [
"dist",
"src",
"README.md",
"LICENSE"
],
"scripts": {
"build": "rollup -c",
"dev": "rollup -c -w",
"test": "jest",
"test:watch": "jest --watch",
"lint": "eslint src/**/*.js",
"docs": "jsdoc -c jsdoc.config.json",
"prepublishOnly": "npm run build && npm test"
},
"keywords": [
"window-manager",
"desktop",
"ui",
"windowing",
"modal",
"context-menu",
"draggable",
"resizable"
],
"author": "Your Name <your.email@example.com>",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/yourusername/web-window-manager.git"
},
"bugs": {
"url": "https://github.com/yourusername/web-window-manager/issues"
},
"homepage": "https://github.com/yourusername/web-window-manager#readme",
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-terser": "^0.4.0",
"eslint": "^8.0.0",
"jest": "^29.0.0",
"jsdoc": "^4.0.0",
"rollup": "^3.0.0",
"rollup-plugin-css-only": "^4.0.0"
},
"peerDependencies": {},
"engines": {
"node": ">=14.0.0"
}
}
Build Configuration (Rollup)
// rollup.config.js
import resolve from '@rollup/plugin-node-resolve';
import terser from '@rollup/plugin-terser';
import css from 'rollup-plugin-css-only';
export default [
// ES Module build
{
input: 'src/index.js',
output: {
file: 'dist/web-window-manager.esm.js',
format: 'es',
sourcemap: true
},
plugins: [resolve()]
},
// UMD build
{
input: 'src/index.js',
output: {
file: 'dist/web-window-manager.js',
format: 'umd',
name: 'WindowManager',
sourcemap: true
},
plugins: [resolve()]
},
// Minified UMD build
{
input: 'src/index.js',
output: {
file: 'dist/web-window-manager.min.js',
format: 'umd',
name: 'WindowManager',
sourcemap: true
},
plugins: [resolve(), terser()]
},
// CSS
{
input: 'src/styles/index.js',
output: {
file: 'dist/web-window-manager.css',
format: 'es'
},
plugins: [css({ output: 'web-window-manager.css' })]
}
];
Documentation Standards
API Documentation (JSDoc)
/**
* Creates a new window instance
* @param {Object} options - Window configuration options
* @param {string} options.title - Window title
* @param {number} [options.width=400] - Window width in pixels
* @param {number} [options.height=300] - Window height in pixels
* @param {Object} [options.position] - Initial window position
* @param {number} [options.position.x=100] - X coordinate
* @param {number} [options.position.y=100] - Y coordinate
* @param {boolean} [options.resizable=true] - Enable window resizing
* @param {boolean} [options.draggable=true] - Enable window dragging
* @param {Function} [options.onClose] - Close event handler
* @returns {Window} The created window instance
* @fires WindowManager#window:created
* @example
* const window = wm.createWindow({
* title: 'My Application',
* width: 600,
* height: 400,
* onClose: () => console.log('Window closed')
* });
*/
createWindow(options = {}) {
// Implementation
}
README Structure
# Web Window Manager
A modern, lightweight window management library for web applications.
## Features
- 🪟 **Draggable Windows** - Smooth window dragging with touch support
- 📐 **Resizable Windows** - Resize from any edge or corner
- 🎨 **Themeable** - CSS variables for easy customization
- ♿ **Accessible** - Full keyboard navigation and ARIA support
- 📱 **Responsive** - Works on desktop and mobile devices
- 🚀 **Performant** - Optimized rendering and minimal reflows
- 🔧 **Framework Agnostic** - Works with any or no framework
## Installation
bash
npm install web-window-manager
## Quick Start
javascript
import WindowManager from 'web-window-manager';
import 'web-window-manager/dist/web-window-manager.css';
const wm = new WindowManager();
const window = wm.createWindow({
title: 'Hello World',
content: '
Welcome!
'});
## API Reference
See [full API documentation](./docs/api.md).
## Examples
- [Basic Usage](./examples/basic.html)
- [Custom Themes](./examples/themes.html)
- [React Integration](./examples/react.html)
- [Vue Integration](./examples/vue.html)
## Browser Support
- Chrome/Edge 88+
- Firefox 78+
- Safari 14+
- iOS Safari 14+
- Chrome Android 88+
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup and guidelines.
## License
MIT © [Your Name]
Type Definitions
// types/index.d.ts
declare module 'web-window-manager' {
export interface WindowOptions {
title?: string;
width?: number;
height?: number;
position?: { x: number; y: number };
resizable?: boolean;
draggable?: boolean;
closable?: boolean;
minimizable?: boolean;
maximizable?: boolean;
content?: string | HTMLElement;
onClose?: () => void;
onFocus?: () => void;
onBlur?: () => void;
}
export interface ContextMenuItem {
label: string;
icon?: string;
action?: () => void;
disabled?: boolean;
separator?: boolean;
submenu?: ContextMenuItem[];
}
export class Window {
id: string;
title: string;
position: { x: number; y: number };
size: { width: number; height: number };
close(): void;
minimize(): void;
maximize(): void;
focus(): void;
setTitle(title: string): void;
setContent(content: string | HTMLElement): void;
}
export default class WindowManager {
constructor(options?: WindowManagerOptions);
createWindow(options?: WindowOptions): Window;
closeWindow(id: string): void;
getWindow(id: string): Window | undefined;
getAllWindows(): Window[];
showContextMenu(event: MouseEvent, items: ContextMenuItem[]): void;
showModal(options: ModalOptions): void;
showNotification(title: string, message: string, duration?: number): void;
}
}
Testing Strategy
Unit Tests
// test/WindowManager.test.js
import WindowManager from '../src/core/WindowManager';
describe('WindowManager', () => {
let wm;
beforeEach(() => {
document.body.innerHTML = '<div id="desktop"></div>';
wm = new WindowManager({ container: '#desktop' });
});
afterEach(() => {
wm.destroy();
});
describe('createWindow', () => {
test('creates window with default options', () => {
const window = wm.createWindow();
expect(window).toBeDefined();
expect(window.title).toBe('New Window');
expect(wm.windows.size).toBe(1);
});
test('creates window with custom options', () => {
const options = {
title: 'Test Window',
width: 500,
height: 400
};
const window = wm.createWindow(options);
expect(window.title).toBe('Test Window');
expect(window.size.width).toBe(500);
expect(window.size.height).toBe(400);
});
});
describe('window interactions', () => {
test('focuses window on click', () => {
const window1 = wm.createWindow();
const window2 = wm.createWindow();
expect(wm.activeWindow).toBe(window2);
window1.element.dispatchEvent(new MouseEvent('mousedown'));
expect(wm.activeWindow).toBe(window1);
});
});
});
Integration Tests
// test/integration/drag-drop.test.js
import WindowManager from '../src/index';
import { simulateDrag } from './helpers';
describe('Drag and Drop Integration', () => {
test('window can be dragged', async () => {
const wm = new WindowManager();
const window = wm.createWindow({
position: { x: 100, y: 100 }
});
const initialPosition = { ...window.position };
await simulateDrag(window.element.querySelector('.window-header'), {
from: { x: 150, y: 120 },
to: { x: 250, y: 220 }
});
expect(window.position.x).toBe(initialPosition.x + 100);
expect(window.position.y).toBe(initialPosition.y + 100);
});
});
Publishing and Distribution
Pre-publish Checklist
-
Version Bump: Update version in
package.json
following semver -
Changelog: Update
CHANGELOG.md
with new features/fixes -
Tests: Ensure all tests pass (
npm test
) -
Build: Generate distribution files (
npm run build
) -
Documentation: Update API docs (
npm run docs
) - Examples: Test all examples with new version
- Types: Verify TypeScript definitions are accurate
Publishing to NPM
# Login to NPM
npm login
# Publish (runs prepublishOnly script automatically)
npm publish
# Tag release on GitHub
git tag v1.0.0
git push origin v1.0.0
CDN Distribution
The package will be automatically available on CDNs:
<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/web-window-manager@1.0.0/dist/web-window-manager.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/web-window-manager@1.0.0/dist/web-window-manager.css">
<!-- unpkg -->
<script src="https://unpkg.com/web-window-manager@1.0.0/dist/web-window-manager.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/web-window-manager@1.0.0/dist/web-window-manager.css">
Best Practices and Lessons Learned
Performance Optimization
- Batch DOM Operations: Group DOM changes to minimize reflows
- Event Delegation: Use single event listeners on parent elements
- RequestAnimationFrame: Throttle drag/resize operations
-
CSS Containment: Use
contain
property for better rendering performance
Accessibility Considerations
- Keyboard Navigation: Implement focus trapping and tab order
- ARIA Labels: Add appropriate roles and labels
- Screen Reader Support: Announce window state changes
- High Contrast Mode: Ensure visibility in all color modes
Common Pitfalls to Avoid
- Memory Leaks: Always remove event listeners on window close
- Z-Index Wars: Use a centralized z-index management system
- Touch vs Mouse: Handle both pointer types consistently
- CSS Conflicts: Use scoped class names or CSS modules
Future Enhancements
- Window Snapping: Snap to screen edges and other windows
- Persistence: Save/restore window layouts
- Multi-Monitor Support: Handle different screen configurations
- Virtual Desktops: Multiple window spaces
- Plugin System: Allow third-party extensions
Conclusion
Building a window manager library requires careful consideration of performance, accessibility, and developer experience. By following modern web standards and best practices, we've created a foundation that developers can use to build sophisticated desktop-like applications in the browser.
The key to success lies in:
- Starting with a solid architecture
- Prioritizing performance from the beginning
- Writing comprehensive documentation
- Testing across different scenarios
- Listening to community feedback
This library serves as both a practical tool and a learning resource for developers interested in creating complex UI systems for the web.
Attention writers! If you’ve ever published on Dev.to, you may be eligible for DEV Contributor rewards. Claim your rewards here. for verified Dev.to users only. – Dev.to Community Support