An architectural, data-driven portfolio engine for the minimalist developer. Built with Next.js 15, React 19, and Framer Motion.
Standard portfolios are cluttered. Gitfolio treats your career, projects, and personal growth like a surgical Git-log history. It separates your Life Data (vibe.json) from the Interface, allowing you to maintain a high-end digital presence with zero design overhead.
- ♠ Aggressive Minimalism — A strict monochrome palette designed for clarity and focus.
- ⚡ Next.js 15 + React 19 — Leveraging the bleeding edge of the Vercel ecosystem.
- ◓ Systemic Animations — Staggered Framer Motion entrances that feel fluid and intentional.
- ⬢ Git-Log Aesthetic — A vertical timeline that ditches "cards" for architectural precision.
- 🟢 Live Status — Integrated CSS-pulsing availability indicator.
- 📋 Portable Profile — One-click "Copy vibe.json" functionality for seamless sharing.
# Clone the architecture
git clone https://github.com/umangthapa1/gitfolio.git
cd gitfolio
# Install the ecosystem
npm install
# Initialize development
npm run devOpen http://localhost:3000 in your browser.
🔗 Live Website: https://vibe-check-84g07wo98-umangs-projects-c9068f52.vercel.app
| Feature | Description |
|---|---|
| Git-Log Aesthetic | Vertical timeline with nodes and connecting lines |
| Live Status | Pulsing green dot for availability |
| One-Click Copy | Copy entire vibe.json to clipboard |
| Image Support | Add images to any timeline entry |
| Auto-Save | Changes save automatically to vibe.json |
| Responsive | Works on mobile and desktop |
You are the architect. Control your entire portfolio from a single source of truth in public/vibe.json, or use the built-in admin editor.
npm run devThen open:
- http://localhost:3000 — Your portfolio
- http://localhost:3000/admin — Config editor
In the admin panel:
- Load — Load an existing vibe.json
- Save — Save changes to server
- Download — Download vibe.json file
Edit public/vibe.json directly:
{
"meta": {
"name": "Your Name",
"title": "Your Role",
"status": "available",
"statusMessage": "Open for collaborations"
},
"entries": [
{
"id": "001",
"date": "2026-03",
"title": "Your Achievement",
"description": "What you did and why it matters.",
"category": "Project",
"icon": "rocket",
"highlight": true
}
]
}| Field | Type | Description |
|---|---|---|
meta.name |
string | Your name |
meta.title |
string | Your role/title |
meta.status |
"available" | "busy" | "unavailable" |
Availability status |
meta.statusMessage |
string | Custom status message |
entries[].date |
string | Date in YYYY-MM format |
entries[].title |
string | Entry title |
entries[].description |
string | Detailed description |
entries[].category |
"Project" | "Work" | "Life" | "Learning" |
Category type |
entries[].icon |
string | Lucide icon name (rocket, briefcase, star, mic, palette, etc.) |
entries[].image |
string | Image URL to display |
entries[].highlight |
boolean | Whether to emphasize this entry |
| Layer | Technology |
|---|---|
| Engine | Next.js 15 (App Router) |
| UI Library | React 19 |
| Styling | Tailwind CSS (Grayscale System) |
| Motion | Framer Motion |
| Icons | Lucide React |
| Type Safety | TypeScript 5+ |
{
"next": "^15.1.0",
"react": "^19.0.0",
"framer-motion": "^11.15.0",
"lucide-react": "^0.469.0"
}src/
├── app/ # Next.js 15 App Router
│ ├── globals.css # Global styles & Tailwind
│ ├── layout.tsx # Root layout with fonts
│ └── page.tsx # Homepage
├── components/
│ └── vibe-timeline/ # Timeline component library
│ ├── VibeTimeline.tsx # Main container
│ ├── TimelineEntry.tsx # Individual entry with animations
│ ├── TimelineHeader.tsx # Header with copy button
│ ├── CategoryBadge.tsx # Category labels
│ ├── LiveDot.tsx # Status indicator
│ ├── CopyToast.tsx # Toast notification
│ └── index.ts # Barrel exports
├── types/
│ └── vibe.ts # TypeScript definitions
└── public/
└── vibe.json # Your profile data
public/vibe.jsonis imported directly insrc/app/page.tsx- Data is type-asserted against the
VibeConfiginterface - Passed to
<VibeTimeline data={vibeConfig} /> - Each entry renders with staggered Framer Motion animations
- Entrance: Staggered animations### Animation System
— each entry delays by
index * 0.1s - Easing: Custom cubic-bezier
[0.25, 0.46, 0.45, 0.94] - Viewport: Entries animate when scrolled into view (
whileInView) - Status Dot: CSS keyframe
pulse-greenanimation
The "Copy my vibe.json" button:
- Serializes the config to formatted JSON
- Uses
navigator.clipboard.writeText() - Shows toast notification with Framer Motion
- Auto-resets after 2.5 seconds
Edit public/vibe.json and add entries to the entries array:
{
"id": "unique-id",
"date": "2026-04",
"title": "New Achievement",
"description": "Description here",
"category": "Work",
"icon": "briefcase",
"highlight": false
}rocket, briefcase, star, mic, palette, mapPin, bookOpen, building, code, coffee, heart, zap
Project (dark), Work (medium), Life (light), Learning (subtle)
npm run devnpm run build # Creates optimized build
npm start # Serve production buildClick the Deploy to Vercel button at the top, or:
# Push to GitHub, then:
npx vercel| Issue | Status | Solution |
|---|---|---|
| 404 for favicon.ico | cosmetic | Add public/favicon.ico |
| Git push auth failed | pending | Run git push manually with credentials |
Built by Umang Thapa | Licensed under MIT
Inspired by Git-log aesthetics and Scandinavian minimalism.

