Syauqi OSPortfolio
← All case studies

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.

Early prototype of the window manager state machine.
Early prototype of the window manager state machine.

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);
};
App shell state that drives the entire interaction model

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.

← More case studies