A personal website built with React and Vite. The site opens with an immersive first-person 3D maze experience and then routes into three content pages: About, Portfolio, and Blog.
- React 19
- Vite
- React Router
- MDX
- Tailwind CSS 4
- Three.js
@react-three/fiber@react-three/drei
The frontend uses a hybrid styling system:
- Tailwind CSS for layout, spacing, sizing, responsive behavior, and most component styling
- custom CSS in
src/index.cssfor global tokens, shared glass/effect classes, and small visual details only
Install dependencies:
pnpm installRun the dev server:
pnpm devCreate a production build:
pnpm buildRun lint:
pnpm lint/-> first-person 3D maze landing experience/about-> About page/portfolio-> Portfolio page/blog-> Blog home/blog/:slug-> Blog post page
src/
App.tsx
main.tsx
index.css
mdx.d.ts
components/
About/
SectionTitle.tsx
common/
Card.tsx
Grid.tsx
PageLayout.tsx
ScrollToTop.tsx
Tag.tsx
TagList.tsx
content/
blog/
react-basics.mdx
pages/
about.mdx
blog-home.mdx
portfolio.mdx
maze/
components/
AmbientAudio.tsx
HUD.tsx
MazeDoor.tsx
MazeMinimap.tsx
MazeScene.tsx
MazeWalls.tsx
Player.tsx
data/
mazeData.ts
pages/
About.tsx
BlogHome.tsx
BlogPost.tsx
Maze.tsx
Portfolio.tsx
utils/
cn.ts
search.ts
src/main.tsxmounts the app insideBrowserRoutersrc/App.tsxdefines the route tablesrc/components/common/ScrollToTop.tsxresets scroll position on route changes
The landing page is a first-person Three.js maze with:
- WASD movement + mouse look
- visible hands/forearms
- ambient audio
- a top-left minimap with route guidance
- a single exit door that opens on
Enterand redirects to/about
Key files:
src/pages/Maze.tsxsets up the R3F canvas and door-to-route transitionsrc/maze/components/MazeScene.tsxassembles lighting, atmosphere, ground, debris, walls, door, player, and audiosrc/maze/components/Player.tsxhandles FPS movement, bobbing, hand sway, pointer lock, and collisionsrc/maze/components/MazeWalls.tsxrenders the tall maze wallssrc/maze/components/MazeDoor.tsxrenders and animates the exit doorsrc/maze/components/MazeMinimap.tsxrenders the guidance mapsrc/maze/components/HUD.tsxrenders the menu, prompts, and minimap shellsrc/maze/components/AmbientAudio.tsxcreates the ambient sound bedsrc/maze/data/mazeData.tsowns maze generation, player spawn/view, exit location, collision helpers, and minimap route helpers
src/pages/About.tsx,src/pages/Portfolio.tsx, andsrc/pages/BlogHome.tsxare route wrapperssrc/components/common/PageLayout.tsxprovides the shared page shell and navbar- content for those pages lives in
src/content/pages/*.mdx
- blog post content lives in
src/content/blog/ - the blog list lives in
src/content/pages/blog-home.mdx src/pages/BlogPost.tsxcurrently renders a specific MDX post directly
That means adding another post still requires both:
- adding/updating the MDX file in
src/content/blog/ - updating the blog route wiring in
src/pages/BlogPost.tsx
For files in src/content/pages/:
- keep editable page data in exported variables near the top of the file
- render the UI from those variables below
- prefer mapping arrays/objects instead of hardcoding repeated markup
This keeps content edits maintainable and reduces layout drift.
Search is enabled on:
- Portfolio
- Blog home
Implementation notes:
- page-level search state lives in
src/pages/Portfolio.tsxandsrc/pages/BlogHome.tsx - shared filtering logic lives in
src/utils/search.ts - the shared navbar search UI lives in
src/components/common/PageLayout.tsx
Update the corresponding MDX file:
src/content/pages/about.mdxsrc/content/pages/portfolio.mdxsrc/content/pages/blog-home.mdx
- Add or edit the MDX file in
src/content/blog/ - Update the post listing data in
src/content/pages/blog-home.mdx - Update
src/pages/BlogPost.tsxso the route renders the intended post
Start here:
src/maze/data/mazeData.tssrc/maze/components/MazeScene.tsxsrc/maze/components/Player.tsxsrc/maze/components/MazeDoor.tsxsrc/maze/components/HUD.tsx
Typical maze changes include:
- maze layout / scale
- player spawn or initial view
- door position and route target
- lighting / atmosphere
- minimap behavior
- collision and movement feel
- keep personal pages and maze changes isolated unless a task explicitly spans both
- prefer shared utilities/components over duplicating logic
- for
src/content/pages/*.mdx, keep content in variables and render from data - treat responsive behavior as a default requirement, not a follow-up fix