Introduction
When I set out to build my personal site, I knew I didn’t want a generic portfolio page. I wanted something that showcased not just what I’ve built, but how I think about engineering problems. The result is mtsaga.net — a platform that includes a full Windows 11-style desktop environment, a PowerShell terminal emulator, an interactive file explorer, and a blog — all stitched together by a unified design system.
This post breaks down the architecture, key engineering decisions, and lessons learned building each major feature.
Site Architecture Overview
The site is a hybrid architecture combining multiple technologies:
| Layer | Technology | Purpose |
|---|---|---|
| Blog & Pages | Jekyll (GitHub Pages) | Static content, blog posts, SEO |
| Desktop UI | Next.js + TypeScript | Windows 11 environment |
| Terminal | Vanilla JavaScript | PowerShell emulator |
| Explorer | Vanilla JavaScript | File browser UI |
| Design System | CSS Custom Properties | Unified visual language |
| Deployment | GitHub Pages + Cloudflare | CDN, SSL, caching |
The key decision was not making the entire site a single SPA. Each feature is self-contained — the blog is statically generated by Jekyll for SEO and speed, while the desktop environment is a separate Next.js app under /desktop/. This gives us the best of both worlds: fast, crawlable content and rich interactive experiences.
Why This Hybrid Approach?
- SEO: Blog posts need to be statically rendered and crawlable. Jekyll does this perfectly.
- Performance: The main site loads in under 1 second. The desktop app only loads when someone navigates to
/desktop/. - Independence: Each feature can be developed, tested, and deployed independently.
- Simplicity: No complex build orchestration — Jekyll handles the outer shell, Next.js handles the desktop.
The Desktop Environment
The crown jewel of the site is a fully functional Windows 11-style desktop that runs entirely in the browser.
Window Management
The window manager handles:
- Drag and drop positioning with boundary constraints
- Resize from all edges and corners
- Minimize to taskbar with animation
- Maximize with double-click title bar toggle
- Z-index stacking with focus management
- Snap layouts for window tiling
Each window is a React component that maintains its own state (position, size, minimized/maximized). The window manager coordinates z-index ordering so the focused window is always on top.
// Simplified window state management
interface WindowState {
id: string;
title: string;
position: { x: number; y: number };
size: { width: number; height: number };
isMinimized: boolean;
isMaximized: boolean;
zIndex: number;
}
Virtual File System
The desktop includes a virtual file system that maps to real content on the site:
/Desktop/→ Desktop icons and shortcuts/Documents/→ Resume, certifications/Projects/→ Portfolio items/Blog/→ Links to blog posts
This is backed by a JSON data structure that mirrors a real file tree, allowing the Explorer app to navigate it and the Terminal to cd through it.
Start Menu & Taskbar
The taskbar tracks open windows, shows a clock, and provides system tray icons. The Start Menu is a searchable app launcher that filters through available “applications” (About, Resume, Projects, Blog, etc.).
Service Worker & Offline
A custom service worker (serviceworker.js) provides:
- Cache-first strategy for static assets (images, fonts, CSS)
- Network-first strategy for HTML pages
- Offline fallback page when connectivity is lost
- App manifest for PWA “Add to Home Screen” support
// Cache strategy in serviceworker.js
self.addEventListener('fetch', event => {
if (event.request.destination === 'image' ||
event.request.url.includes('/assets/')) {
event.respondWith(cacheFirst(event.request));
} else {
event.respondWith(networkFirst(event.request));
}
});
Performance Considerations
The desktop app uses Next.js code splitting aggressively. Each “application” (About, Resume, etc.) is a separate chunk that only loads when the window opens. This keeps the initial bundle small despite the complexity of the environment.
The Terminal Emulator
The terminal at /terminal/ is a custom-built PowerShell-style emulator written in vanilla JavaScript — no frameworks, no dependencies.
Command Parser
The parser tokenizes input into command name and arguments, handling:
- Quoted strings (
"hello world") - Pipe operators (
|) - Flag parsing (
-Name value,-Recurse) - Tab completion with cycling
// Tokenizer approach
function tokenize(input) {
const tokens = [];
let current = '';
let inQuote = false;
for (const char of input) {
if (char === '"') { inQuote = !inQuote; continue; }
if (char === ' ' && !inQuote) {
if (current) tokens.push(current);
current = '';
continue;
}
current += char;
}
if (current) tokens.push(current);
return tokens;
}
Built-in Commands
The terminal supports a full set of commands:
| Command | Description |
|---|---|
Get-Help |
Shows available commands |
Get-ChildItem / ls |
Lists directory contents |
Set-Location / cd |
Changes directory |
Get-Content / cat |
Reads file contents |
Clear-Host / clear |
Clears the terminal |
Get-Process |
Shows “running” processes |
Get-Certification |
Lists my certifications |
Get-Experience |
Shows work experience |
Each command is a self-contained function that receives parsed arguments and returns formatted output. Output rendering supports tables, colored text, and ASCII art.
Tab Completion
The tab completion engine indexes:
- All available command names
- Directories/files in the current path (from the virtual FS)
- Command-specific parameter names
Pressing Tab cycles through matches. Pressing it again cycles to the next match (PowerShell-style).
Output Rendering
Terminal output isn’t plain text — it’s HTML rendered to look like PowerShell output. This means we can do colored text, tables with alignment, clickable links, and even inline ASCII art, all while maintaining the terminal aesthetic.
The Design System
The design system ensures visual consistency across Jekyll pages, the Next.js desktop, and all interactive features.
CSS Custom Properties
Everything starts with CSS variables defined in :root:
:root {
--dark-bg: #161B33;
--section-bg: #1c2948;
--card-bg: #22305a;
--fg: #e4e6eb;
--muted: #8a9bb8;
--accent: #00aaff;
--btn-bg: #00aaff;
--footer-bg: #0f1724;
}
These tokens are shared across every CSS file on the site. When I want to adjust the accent color or background, it’s a single-line change that propagates everywhere.
Typography
The site uses two font families:
- Orbitron (Google Fonts): For headings, navigation, buttons — gives the “cyber” feel
- Segoe UI (system): For body text, ensuring readability
The hierarchy is:
h1: 2.5–3rem Orbitronh2: 1.8–2rem Orbitron, accent color, bottom borderh3: 1.5rem Orbitron- Body: 1rem Segoe UI, 1.8 line-height
Component Library
Key reusable components include:
- Cards (
.blog-card,.sidebar-widget): Consistent rounded corners, shadows, hover effects - Tags (
.blog-tag,.tag-cloud-item): Pill-shaped with hover state transitions - Buttons: Accent-colored with glow hover effects
- Navigation: Sticky header with backdrop blur
Accessibility
The design system follows WCAG 2.1 AA standards:
- Color contrast ratios are 4.5:1+ for body text
- Interactive elements have visible focus indicators
- All images have alt text
- Semantic HTML structure throughout
- Keyboard navigable menus and interactive elements
Lessons Learned
1. Start Simple, Then Layer Complexity
The site started as a single index.html. The blog was added next, then the terminal, then the desktop. Each layer was built on a stable foundation. If I’d tried to build everything at once, I’d still be in development.
2. Don’t Over-Abstract Early
I resisted the urge to create a shared component library across Jekyll and Next.js. They share CSS variables, but that’s it. Trying to share React components with a static site generator would have been an unnecessary coupling.
3. Performance Is a Feature
Every interactive feature is lazy-loaded. The desktop doesn’t load until you visit /desktop/. The terminal doesn’t load until you visit /terminal/. The main site stays fast because it’s just HTML and CSS.
4. Build for Fun, Document Later
Most of these features started as “what if I could…” experiments. The design system post and this deep dive came after. Building something that excites you first, then documenting it, produces better results than designing a documentation-first architecture.
What’s Next
Upcoming work on mtsaga.net:
- Interactive Security Labs — In-browser hash cracking, packet analysis, and AD graph visualization tied to blog posts
- Search & Topic Graph — Full-text search with a D3.js force-directed graph showing how topics connect (already live)
- Lab Notebook — A lightweight stream of short updates on what I’m testing and building
- Enhanced Terminal — More commands, piping between commands, and history persistence
If you have questions about any of these systems, feel free to reach out at mt@mtsaga.net.