A high-performance, immersive portfolio built with React, Three.js (Fiber), and Tailwind CSS.
This project represents a complete architectural overhaul from a static HTML website to a scalable Single Page Application (SPA). It features physics-based 3D interactions, cinema-quality page transitions, and a serverless contact engine.
Originally, this portfolio was built using vanilla HTML, CSS, and JavaScript. While functional, it suffered from scalability issues common in static sites:
- Monolithic Files:
index.htmlwas 500+ lines long. Adding a project meant copy-pasting 30 lines of HTML code manually. - Global CSS Chaos: Styles were global, leading to naming conflicts where changing a
.cardclass would break layouts in unrelated sections. - Imperative DOM Manipulation: JavaScript had to manually query selectors (
document.querySelector) to handle basic interactions like mobile menus.
I re-engineered the site as a data-driven React application. This shift solved the maintenance nightmare and improved User Experience (UX) significantly.
| Feature | Old Version (HTML/CSS) | New Version (React Architecture) | Engineering Value |
|---|---|---|---|
| Data Management | Hardcoded HTML blocks. Copy-paste to add projects. | Data-Driven (projects.js). Single source of truth. |
Scalability: Add a new project by adding one JSON object. The UI renders automatically. |
| State | Manual DOM manipulation. | Declarative State (useState). |
Reliability: React guarantees UI sync with state. No "desynchronized" interface bugs. |
| Routing | Multiple .html files with repeated navbars. |
Client-Side Routing (react-router-dom). |
Performance: True SPA. No full page reloads. Instant transitions. |
| UX & Motion | Basic CSS :hover. Jarring page jumps. |
Lifecycle Animations. | Polish: Components animate out (slide down) before unmounting, impossible with static CSS. |
| Contact | mailto: link (relied on user's email client). |
Integrated Form (Formspree). | Lead Gen: Serverless backend handles emails directly in-app. |
To ensure 60fps performance, I separated the 3D physics engine from the DOM interface.
- Background Layer (
z-index: 0): Runs the heavyreact-three-fiberphysics simulation (Lanyard). - Foreground Layer (
z-index: 10): A lightweightOverlay.jsxcomponent that handles all user interactions (scrolling, clicking, forms).
Benefit: Complex UI interactions never block the WebGL render loop.
React Router unmounts components instantly, making "exit" animations difficult. I engineered a delay system to solve this.
The Logic:
- User clicks "Back".
- State changes:
setIsExiting(true). - CSS Animation
slideDownFadeplays (0.5s). setTimeoutwaits 500ms, then triggersMaps('/').
/* The Exit Animation */
@keyframes slideDownFade {
from { transform: translateY(0); opacity: 1; }
to { transform: translateY(100vh); opacity: 0; }[Portfolio Development Journey - Complete README.pdf](https://github.com/user-attachments/files/24270382/Portfolio.Development.Journey.-.Complete.README.pdf)
}I replaced the unreliable mailto: link with a fully styled form connected to the Formspree API.
Async/Await: Uses fetch() to post data without reloading the page.
Validation: Prevents submission of empty fields.
Feedback Loop: The UI updates through three distinct states: Idle -> Submitting -> Success.
The submit button uses SVG geometry morphing to provide visual feedback, implemented purely in CSS/React (no heavy GSAP libraries).
Morphing: Transitions from a width: 190px rectangle to a width: 60px circle.
Spinner: Uses stroke-dasharray offset animation to create a spinning loader.
Stability: Utilizes transform-box: fill-box to ensure the spinner rotates around its own center, eliminating SVG wobble.
📂 Project Structure A clean, modular file structure separating logic, data, and presentation.
src/
├── assets/ # Static images (optimized WebP)
├── components/
│ ├── Lanyard.jsx # 3D Physics Simulation (R3F)
│ ├── Overlay.jsx # Main UI (Nav, Hero, Contact Form)
│ ├── ProjectCard.jsx # Reusable Card Component
│ └── Loader.jsx # Suspense Fallback
├── data/
│ └── projects.js # Single Source of Truth for Project Data
├── pages/
│ └── ProjectPage.jsx # Dynamic Detail Page (Routing target)
├── App.jsx # Routes & Layout Logic
├── App.css # Scoped Keyframe Animations
└── main.jsx # Entry Point
"I originally built this portfolio with static HTML and CSS, but I hit a wall with scalability—adding a new project meant manually updating code in five different files.
I re-architected it as a React Single Page Application. I moved all data into a JSON structure so the UI renders dynamically. The biggest engineering challenge was the page transitions. Since React unmounts components instantly, I had to implement a custom lifecycle hook using setTimeout to allow the 'Slide Down' animation to finish before the route changed.
I also optimized the performance by separating the 3D WebGL layer from the UI layer, ensuring that the physics simulation doesn't cause input lag on the contact form."
- Setup & Installation
-
Clone the repository Terminal:
git clone [https://github.com/yourusername/portfolio.git](https://github.com/yourusername/portfolio.git) -
Install Dependencies Terminal:
npm install -
Run Development Server Terminal:
npm run dev -
Build for Production Terminal:
npm run build