Personal portfolio built with Next.js App Router, TypeScript, and Tailwind CSS. It features JSON‑driven content, Markdown‑powered blog posts, and a retro terminal aesthetic with a CSS 3D spinning globe.
Highlights
- Modern App Router architecture under
src/app/
- JSON content in
src/content/
and Markdown posts insrc/_posts/
- GFM blog with code highlighting, captions, and anchored headings
- CSS‑only 3D wireframe globe component (no WebGL/three.js)
- Framework: Next.js (App Router)
- UI: React + Tailwind CSS
- Language: TypeScript (strict)
- Markdown: gray-matter + unified (remark/rehype) + rehype-pretty-code
src/app/
: Next.js App Router pages, routes, and UI blockspage.tsx
: Home view wiring the left profile/retro globe and right content blocksblog/
: Blog index routeposts/[id]/page.tsx
: Dynamic route for individual Markdown posts- UI Blocks:
AboutBlock.tsx
,ExperienceBlock.tsx
,ProjectsBlock.tsx
,BlogLinkBlock.tsx
,SideBar.tsx
,Socials.tsx
src/_posts/
: Markdown posts consumed bysrc/lib/api.tsx
src/content/
: User‑editable JSON for About, Experience, Projectssrc/lib/api.tsx
: Markdown parsing pipeline and post helperspublic/
: Static assets (images, icons)- Config:
next.config.js
,tailwind.config.ts
,tsconfig.json
,amplify.yml
The globe is a pure CSS 3D wireframe sphere rendered via nested divs and transforms. No WebGL or canvas is used.
- Component:
src/app/RetroGlobe.tsx
- Styles:
src/app/globals.css
(search for “RETRO GLOBE 3D WIREFRAME”) - How it works:
- Renders latitude rings and longitude meridians as bordered circles positioned/rotated in 3D (
rotateX
/rotateY
) - Continuous rotation via
@keyframes globe-rotate
; subtle tilt via@keyframes globe-wobble
- Atmospheric effects with
.globe-glow
and gradient shading - Mount guard avoids hydration mismatches in SSR (
mounted
state)
- Renders latitude rings and longitude meridians as bordered circles positioned/rotated in 3D (
- Customize:
- Size: change
const R = 120
inRetroGlobe.tsx
and matching.meridian-line
width/height in CSS - Speed: tweak animation durations in
globals.css
(globe-rotate
,globe-wobble
) - Theme: adjust Tailwind tokens and
.latitude-ring
/.meridian-line
borders
- Size: change
- Mobile/perf:
- Uses
translate3d
and tunedperspective
to force GPU acceleration - iOS fallbacks increase border thickness and adjust perspective for clarity
- Uses
- Install deps:
npm ci
(ornpm install
) - Dev server:
npm run dev
→ http://localhost:3000 - Build:
npm run build
→ outputs.next/
- Start:
npm start
→ serves the production build - Lint:
npm run lint
- Optional:
nix develop
for a preconfigured Node 20 dev shell
All non‑code content lives in src/content
(JSON) and src/_posts
(Markdown).
- About (
src/content/about.json
)- Shape:
{ "paragraphs": ["..."] }
- Shape:
- Experience (
src/content/experience.json
)- Items contain:
start
,end
,company
,company_link
,title
,body[]
- Items contain:
- Projects (
src/content/projects.json
)- Items contain:
title
,summary
,image
,url
,github
- Items contain:
- Location:
src/_posts/*.md
- Frontmatter example:
--- title: My Awesome Blog Post date: 2025-08-16 image: /blog/my-post-featured-image.webp ---
- Features:
- GitHub Flavored Markdown
- Syntax highlighting (
rehype-pretty-code
) - Image captions via italic line after image (figure + figcaption)
- Linkable headings via
rehype-slug
+ autolink
- Note: Markdown parser creation is memoized for faster builds
- Config:
amplify.yml
- Steps:
npm ci
→npm run build
- Artifact: deploy
.next/
directory - Cache:
.next/cache/**/*
,.npm/**/*
- Env vars via Amplify; expose only public values with
NEXT_PUBLIC_
- Images:
images.unoptimized = true
(optimize sources; prefer WebP) - Avoid large binaries in
public/
This site uses proprietary fonts from https://usgraphics.com, which are gitignored. The site will fallback to JetBrains Mono if the custom fonts are not provided in the proper directory.