Skip to content

Commit ced5628

Browse files
committed
Fresh Modern Portfolio-v3
1 parent 9679d01 commit ced5628

20 files changed

Lines changed: 586 additions & 267 deletions

README.md

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,51 @@
1-
# Krishna Andhe — Portfolio (v3)
1+
# Krishna Andhe — Portfolio (v5)
22

3-
Interactive, animated single-page portfolio with light/dark theming.
3+
Interactive, animated single-page portfolio / living resume with light/dark theming.
44
Live at **[krishnaandhe.github.io](https://krishnaandhe.github.io)**.
55

66
## ✨ Features
7-
- 🪪 **Custom KA logo** (SVG) in navbar + favicon
8-
- 🌗 **Light / Dark theme toggle** (remembers choice + respects system)
9-
- 🖱️ **Custom animated cursor** with hover reveal (desktop)
10-
- 📊 **Scroll progress bar** + scroll-triggered animations
11-
- 🫧 **Animated gradient hero** with scroll-hint chevron
12-
- 🏷️ **Tech marquee** with brand icons + names
13-
- 🗂️ **Selected Work** cards — icon + app name
14-
- 🧊 **3D tilt cards** for Experience & Contact socials
15-
- 📇 **Multi-handle contact grid** (GitHub, LinkedIn, X, Telegram, WhatsApp, Email)
7+
- 🖼️ **2-column hero** with profile photo + rotating gradient ring
8+
- 🪪 **Custom logo** (`site_logo.png`) in navbar + favicon
9+
- 🌗 **Light / Dark theme toggle**
10+
- 🖱️ **Custom animated cursor** (desktop) + scroll progress bar
11+
-**Glowing Tech Stack grid** (static, pulsing glow — no scrolling)
12+
- 🗂️ **Projects** — featured (manual) + **auto-pulled public GitHub repos**
13+
- 🧾 **Expandable Experience timeline** — click a role to reveal resume details
14+
- 🧊 **3D tilt contact cards** (multi-handle)
1615

1716
## 🚀 Stack
1817
Next.js 15 · React 19 · Tailwind CSS 3 · Framer Motion 11 · next-themes · react-icons
1918

20-
## ✍️ Edit Content
21-
Everything lives in one file:
22-
```
23-
data/portfolio.js
19+
## 🖼️ Photo & Logo
20+
- Photo: put your image at `public/profile.jpg`, then set `photo: "/profile.jpg"` in `data/portfolio.js`.
21+
- Logo: replace `public/site_logo.png` with your own (navbar + favicon use it).
22+
23+
## 🔗 GitHub Auto-Pull (Projects)
24+
Configured in `data/portfolio.js``github`:
25+
```js
26+
export const github = {
27+
username: "krishnaandhe",
28+
enabled: true, // false = turn off auto-pull
29+
max: 6, // how many repos to show
30+
exclude: ["krishnaandhe.github.io", "krishnaandhe"],
31+
includeForks: false,
32+
onlyTopic: "", // e.g. "showcase" → only repos with this GitHub topic
33+
sort: "updated", // "updated" | "stars"
34+
};
2435
```
25-
Name, tagline, stats, socials, tech stack, projects, services, experience.
26-
Icons are string keys mapped in `components/icons.js`.
36+
Repos are fetched **in the browser** from the public GitHub API — no server needed.
37+
New public repos appear automatically. `featuredProjects` (e.g. private Route Arc)
38+
always show first.
39+
40+
> Tip: to curate, tag chosen repos with a GitHub **topic** (e.g. `showcase`) and set
41+
> `onlyTopic: "showcase"`. Only those will appear.
42+
43+
## 🧾 Experience (Resume Details)
44+
Edit `experience` in `data/portfolio.js`. Each role supports `summary`, a `details`
45+
array (bullets shown on expand) and `tags`.
2746

2847
## 🎨 Re-theme
29-
Colors live in `app/globals.css``.light` and `.dark` (RGB triplets).
30-
Change `--accent` / `--accent2` to re-skin both themes.
48+
Colors live in `app/globals.css``.light` / `.dark` (RGB triplets).
3149

3250
## 🛠️ Local Dev
3351
```bash
@@ -37,7 +55,7 @@ npm run dev # http://localhost:3000
3755

3856
## 📦 Deploy
3957
```bash
40-
npm run build # static ./out
58+
npm run build
4159
git add . && git commit -m "update" && git push
4260
```
4361
Auto-deploys via GitHub Actions. Ensure **Settings → Pages → Source = GitHub Actions**.

app/layout.jsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Providers from "@/components/Providers";
55
export const metadata = {
66
title: `${profile.name}${profile.role}`,
77
description: profile.tagline,
8-
icons: { icon: "/favicon.svg" },
8+
icons: { icon: "/site_logo.png" },
99
openGraph: {
1010
title: `${profile.name} — Portfolio`,
1111
description: profile.tagline,
@@ -16,7 +16,9 @@ export const metadata = {
1616
export default function RootLayout({ children }) {
1717
return (
1818
<html lang="en" suppressHydrationWarning>
19-
<body>
19+
{/* suppressHydrationWarning on body too: some browser extensions inject
20+
attributes onto <body> before React hydrates (harmless). */}
21+
<body suppressHydrationWarning>
2022
<Providers>{children}</Providers>
2123
</body>
2224
</html>

app/page.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import CustomCursor from "@/components/CustomCursor";
22
import ScrollProgress from "@/components/ScrollProgress";
33
import Navbar from "@/components/Navbar";
44
import Hero from "@/components/Hero";
5-
import Marquee from "@/components/Marquee";
6-
import Work from "@/components/Work";
5+
import Stack from "@/components/Stack";
6+
import Projects from "@/components/Projects";
77
import About from "@/components/About";
88
import Experience from "@/components/Experience";
99
import Contact from "@/components/Contact";
@@ -16,8 +16,8 @@ export default function Home() {
1616
<Navbar />
1717
<main>
1818
<Hero />
19-
<Marquee />
20-
<Work />
19+
<Stack />
20+
<Projects />
2121
<About />
2222
<Experience />
2323
<Contact />

components/About.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export default function About() {
3737
className="glass rounded-xl p-5 hover:border-accent/60 transition-colors"
3838
>
3939
<div className="flex items-start gap-3">
40-
{/* Logo + name */}
4140
<span className="mt-0.5 w-10 h-10 shrink-0 rounded-lg bg-gradient-to-br from-accent to-accent2 flex items-center justify-center text-bg">
4241
<Icon size={18} />
4342
</span>

components/Contact.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ export default function Contact() {
3030
</p>
3131
</div>
3232

33-
{/* 3D tilt social cards — scales to many handles */}
3433
<div
3534
className="mt-12 grid grid-cols-2 sm:grid-cols-3 gap-4 max-w-3xl mx-auto"
3635
style={{ perspective: "1000px" }}

components/Experience.jsx

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,117 @@
11
"use client";
2-
import { motion } from "framer-motion";
2+
import { useState } from "react";
3+
import { motion, AnimatePresence } from "framer-motion";
4+
import { FiPlus } from "react-icons/fi";
35
import { experience } from "@/data/portfolio";
46
import { getIcon } from "./icons";
5-
import Tilt from "./Tilt";
7+
8+
function Entry({ e, i, open, onToggle }) {
9+
const Icon = getIcon(e.icon);
10+
return (
11+
<div className="relative pl-10 sm:pl-14">
12+
{/* timeline dot */}
13+
<span className="absolute left-0 sm:left-2 top-2 w-4 h-4 rounded-full bg-gradient-to-br from-accent to-accent2 ring-4 ring-bg" />
14+
15+
<motion.div
16+
initial={{ opacity: 0, y: 20 }}
17+
whileInView={{ opacity: 1, y: 0 }}
18+
viewport={{ once: true, margin: "-60px" }}
19+
transition={{ duration: 0.5, delay: i * 0.08 }}
20+
>
21+
<button
22+
data-hover
23+
onClick={onToggle}
24+
aria-expanded={open}
25+
className="w-full text-left glass rounded-2xl p-6 hover:border-accent/60 transition-colors"
26+
>
27+
<div className="flex items-start gap-4">
28+
<span className="w-11 h-11 shrink-0 rounded-xl bg-gradient-to-br from-accent to-accent2 flex items-center justify-center text-bg">
29+
<Icon size={20} />
30+
</span>
31+
32+
<div className="flex-1 min-w-0">
33+
<div className="flex items-baseline gap-2 flex-wrap">
34+
<h3 className="text-lg font-bold">{e.role}</h3>
35+
<span className="text-accent2 text-sm">· {e.org}</span>
36+
<span className="ml-auto text-xs font-mono text-accent shrink-0">
37+
{e.period}
38+
</span>
39+
</div>
40+
<p className="text-muted text-sm mt-1">{e.summary}</p>
41+
42+
<AnimatePresence initial={false}>
43+
{open && (
44+
<motion.div
45+
initial={{ height: 0, opacity: 0 }}
46+
animate={{ height: "auto", opacity: 1 }}
47+
exit={{ height: 0, opacity: 0 }}
48+
transition={{ duration: 0.35, ease: "easeInOut" }}
49+
className="overflow-hidden"
50+
>
51+
<ul className="mt-4 space-y-2">
52+
{e.details?.map((d, j) => (
53+
<li key={j} className="text-muted text-sm flex gap-2">
54+
<span className="text-accent mt-0.5"></span>
55+
{d}
56+
</li>
57+
))}
58+
</ul>
59+
{e.tags?.length > 0 && (
60+
<div className="mt-4 flex flex-wrap gap-2">
61+
{e.tags.map((t) => (
62+
<span
63+
key={t}
64+
className="text-xs px-2.5 py-1 rounded-md border border-line text-muted"
65+
>
66+
{t}
67+
</span>
68+
))}
69+
</div>
70+
)}
71+
</motion.div>
72+
)}
73+
</AnimatePresence>
74+
75+
<span className="mt-3 inline-flex items-center gap-1.5 text-xs font-medium text-accent">
76+
<motion.span animate={{ rotate: open ? 45 : 0 }}>
77+
<FiPlus size={14} />
78+
</motion.span>
79+
{open ? "Show less" : "Show details"}
80+
</span>
81+
</div>
82+
</div>
83+
</button>
84+
</motion.div>
85+
</div>
86+
);
87+
}
688

789
export default function Experience() {
90+
// First entry open by default; click to toggle each.
91+
const [openIndex, setOpenIndex] = useState(0);
92+
893
return (
994
<section id="experience" className="py-24 border-t border-line">
1095
<div className="container">
1196
<h2 className="text-3xl sm:text-4xl font-bold mb-3">Experience</h2>
12-
<p className="text-muted mb-12">Concise, informative — the essentials.</p>
97+
<p className="text-muted mb-12">
98+
Click any role to expand the details.
99+
</p>
13100

14-
{/* 3D tilt cards that reveal on scroll */}
15-
<div
16-
className="grid sm:grid-cols-3 gap-6"
17-
style={{ perspective: "1000px" }}
18-
>
19-
{experience.map((e, i) => {
20-
const Icon = getIcon(e.icon);
21-
return (
22-
<motion.div
101+
{/* vertical timeline line */}
102+
<div className="relative">
103+
<span className="absolute left-[7px] sm:left-[15px] top-2 bottom-2 w-px bg-line" />
104+
<div className="space-y-6">
105+
{experience.map((e, i) => (
106+
<Entry
23107
key={i}
24-
initial={{ opacity: 0, y: 40, rotateX: -8 }}
25-
whileInView={{ opacity: 1, y: 0, rotateX: 0 }}
26-
viewport={{ once: true, margin: "-60px" }}
27-
transition={{ duration: 0.6, delay: i * 0.12 }}
28-
>
29-
<Tilt
30-
data-hover
31-
className="glass rounded-2xl p-6 h-full hover:border-accent/60 transition-colors"
32-
>
33-
<div style={{ transform: "translateZ(40px)" }}>
34-
<span className="w-11 h-11 rounded-xl bg-gradient-to-br from-accent to-accent2 flex items-center justify-center text-bg mb-4">
35-
<Icon size={20} />
36-
</span>
37-
<div className="flex items-baseline justify-between gap-2">
38-
<h3 className="font-semibold">{e.role}</h3>
39-
<span className="text-xs font-mono text-accent shrink-0">
40-
{e.period}
41-
</span>
42-
</div>
43-
<p className="text-accent2 text-sm mt-0.5">{e.org}</p>
44-
<p className="text-muted text-sm mt-3 leading-relaxed">
45-
{e.summary}
46-
</p>
47-
</div>
48-
</Tilt>
49-
</motion.div>
50-
);
51-
})}
108+
e={e}
109+
i={i}
110+
open={openIndex === i}
111+
onToggle={() => setOpenIndex(openIndex === i ? -1 : i)}
112+
/>
113+
))}
114+
</div>
52115
</div>
53116
</div>
54117
</section>

0 commit comments

Comments
 (0)