Frontend Engineering
Building a Web OS
A phone-like portfolio experience with lock screen, app navigation, gestures, and local-first data.
February 2026 · 10 min read · Syauqi Suhaimi
The concept was simple: portfolio sites feel static, so I designed mine as a small operating system. Instead of scrolling sections, users unlock a phone UI, open apps, and explore my projects through interaction.
The Challenge
How do I implement a responsive, operating system-like interface thats work on browser and mobile? I needed a way to manage global state for multiple 'apps', handle complex animations, and ensure smooth performance across devices.
The Solution
I used App.tsx as the main controller of the phone UI. It handles lock/unlock, which app or folder is open, swipe-to-unlock, and bottom navigation. Shared features like wallpaper and notifications are kept in separate providers so every screen can use them easily.
typescript
const [isLocked, setIsLocked] = useState(true);
const [activeAppId, setActiveAppId] = useState<string | null>(null);
const [activeFolderId, setActiveFolderId] = useState<string | null>(null);
const [folderOrigin, setFolderOrigin] = useState<DOMRect | null>(null);
const handleAppClick = (appId: string, origin?: DOMRect) => {
const appConfig = apps.find((app) => app.id === appId);
if (appConfig?.type === "folder") {
setActiveAppId(null);
setActiveFolderId(appId);
setFolderOrigin(origin ?? null);
return;
}
setActiveFolderId(null);
setFolderOrigin(null);
setActiveAppId(appId);
};Animation and Gesture System
The UX depends on motion, so I used Framer Motion for app open/close transitions, folder zoom from icon origin, and screen-to-screen spring navigation. A custom useSwipe hook handles touch and mouse gestures with a shared threshold so behavior stays consistent across devices.
Interactive Demo: Window Physics
One of the key features is the 'spring' physics when you open or close an app. Try interacting with this mini-window below to see the physics engine in action.
Data and Personalization
To make the portfolio feel like a real device, I added local-first persistence. Gallery media and wallpaper settings are stored in IndexedDB.
Performance and Reliability
I kept the runtime lightweight by rendering only one active app window and using AnimatePresence for controlled mounting/unmounting. This way, even with complex animations, the performance stays smooth on mobile devices.
Results
The final project ships as a cohesive interactive portfolio: lock screen to unlock, swipe-based navigation, app-style content discovery and local media persistence. The architecture is modular enough to add new apps without rewriting the shell.