Animated look (way better)

This commit is contained in:
2025-12-09 11:51:05 +01:00
parent 4fc62d6045
commit 41214ffc94
5 changed files with 252 additions and 83 deletions
+43
View File
@@ -17,6 +17,7 @@
"@notionhq/client": "^2.2.15",
"@types/react-syntax-highlighter": "^15.5.13",
"dotenv": "^16.4.7",
"framer-motion": "^12.23.25",
"katex": "^0.16.19",
"lucide-react": "^0.554.0",
"next": "^16.0.7",
@@ -1600,6 +1601,33 @@
"node": ">=0.4.x"
}
},
"node_modules/framer-motion": {
"version": "12.23.25",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.25.tgz",
"integrity": "sha512-gUHGl2e4VG66jOcH0JHhuJQr6ZNwrET9g31ZG0xdXzT0CznP7fHX4P8Bcvuc4MiUB90ysNnWX2ukHRIggkl6hQ==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.23.23",
"motion-utils": "^12.23.6",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -3273,6 +3301,21 @@
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/motion-dom": {
"version": "12.23.23",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.23.6"
}
},
"node_modules/motion-utils": {
"version": "12.23.6",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
"license": "MIT"
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+1
View File
@@ -18,6 +18,7 @@
"@notionhq/client": "^2.2.15",
"@types/react-syntax-highlighter": "^15.5.13",
"dotenv": "^16.4.7",
"framer-motion": "^12.23.25",
"katex": "^0.16.19",
"lucide-react": "^0.554.0",
"next": "^16.0.7",
+159 -64
View File
@@ -1,5 +1,6 @@
"use client";
import { motion, spring } from "framer-motion";
import PinnedRepos from "@/components/PinnedRepos";
import Skill from "@/components/Skill";
import {
@@ -17,6 +18,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { CoffeeIcon, HeartIcon } from "lucide-react";
import { useEffect, useState } from "react";
import GitHubCalendar from "react-github-calendar";
import { fadeInUp, popIn, staggerContainer } from "@/components/effects";
export default function App() {
const [contribs, setContribs] = useState<number>();
@@ -25,81 +27,154 @@ export default function App() {
const fetchContributions = async () => {
const res = await fetch("/api/contribs");
const data = await res.json();
console.log("Total contributions:", data.total);
setContribs(data.total || undefined);
};
fetchContributions();
}, []);
return (
<div className="w-full max-w-screen-lg mx-auto animate-fadeIn ">
<motion.div
className="w-full max-w-screen-lg mx-auto"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.6 }}
>
<div className="px-8 md:px-10 lg:px-15 xl:px-24">
<h1 className="py-2 text-gray-300">Hey, I'm Klesti! 👋🏻</h1>
<p className="font-semibold text-gray-300">
{/* HEADER */}
<motion.h1
className="py-2 text-gray-300"
variants={fadeInUp}
initial="hidden"
animate="show"
>
Hey, I'm Klesti! 👋🏻
</motion.h1>
<motion.p
className="font-semibold text-gray-300"
variants={fadeInUp}
initial="hidden"
animate="show"
transition={{ delay: 0.15 }}
>
A 15 year old Software Engineer from Tirane, Albania, with 6 years of
experience, specializing in full-stack web development, desktop
applications, TUI apps, and hacking. I also have a deep interest in
low-level programming.
</p>
</motion.p>
<div className="pt-6">
{/* KO-FI BUTTON */}
<motion.div
className="pt-6"
animate={{ y: [0, -3, 0] }}
transition={{ repeat: Infinity, duration: 3 }}
>
<a href="https://ko-fi.com/X8X01OX1WR" target="_blank">
<span className="bg-transparent-ish rounded-md border border-slate-400 p-2 flex w-max gap-2 items-center">
<CoffeeIcon /> Support me on Ko-fi
</span>
</a>
</div>
{/* Socials and skill */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent">
{/* socials */}
<div className="pt-5 flex flex-row gap-5 w-max">
<a href="https://github.com/selimaj-dev/" target="blank_">
<FontAwesomeIcon
icon={faGithub}
className="text-slate-400 size-[2.3rem]"
/>
</a>
<a
href="https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
target="blank_"
<motion.span
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className="bg-transparent-ish rounded-md border border-slate-400 p-2 flex w-max gap-2 items-center cursor-pointer"
>
<FontAwesomeIcon
icon={faLinkedin}
className="text-slate-400 size-[2.3rem]"
/>
</a>
<a href="https://www.instagram.com/selimaj.dev/" target="blank_">
<FontAwesomeIcon
icon={faInstagram}
className="text-slate-400 size-[2.3rem]"
/>
</a>
</div>
<div>
<div className="p-3 rounded-xl">
<div className="mx-auto w-max flex flex-row gap-5">
<Skill skill="Rust" icon={faRust} />
<Skill skill="Go" icon={faGolang} />
<Skill skill="JavaScript" icon={faJs} />
<Skill skill="Java" icon={faJava} classInsert="p-1" />
<Skill skill="Python" icon={faPython} />
<Skill skill="React" icon={faReact} />
</div>
</div>
</div>
</div>
<CoffeeIcon /> Support me on Ko-fi
</motion.span>
</a>
</motion.div>
<h1 className="text-slate-300">Pinned Projects</h1>
<section className="">
{/* SOCIALS + SKILLS */}
<motion.div
className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent"
variants={staggerContainer}
initial="hidden"
animate="show"
>
{/* socials */}
<motion.div className="pt-5 flex flex-row gap-5 w-max">
{[faGithub, faLinkedin, faInstagram].map((icon, i) => (
<motion.a
key={i}
href={
icon === faGithub
? "https://github.com/selimaj-dev/"
: icon === faLinkedin
? "https://www.linkedin.com/in/klesti-selimaj-7a0162343/"
: "https://www.instagram.com/selimaj.dev/"
}
target="blank_"
variants={popIn}
whileHover={{ scale: 1.2, rotate: 3 }}
whileTap={{ scale: 0.9 }}
>
<FontAwesomeIcon
icon={icon}
className="text-slate-400 size-[2.3rem]"
/>
</motion.a>
))}
</motion.div>
{/* skills */}
<motion.div variants={popIn}>
<div className="p-3 rounded-xl">
<motion.div
className="mx-auto w-max flex flex-row gap-5"
variants={staggerContainer}
initial="hidden"
animate="show"
>
<motion.div variants={popIn}>
<Skill skill="Rust" icon={faRust} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="Go" icon={faGolang} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="JavaScript" icon={faJs} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="Java" icon={faJava} classInsert="p-1" />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="Python" icon={faPython} />
</motion.div>
<motion.div variants={popIn}>
<Skill skill="React" icon={faReact} />
</motion.div>
</motion.div>
</div>
</motion.div>
</motion.div>
{/* PINNED PROJECTS */}
<motion.h1
className="text-slate-300 mt-4"
variants={fadeInUp}
initial="hidden"
animate="show"
>
Pinned Projects
</motion.h1>
<motion.section>
<PinnedRepos />
</section>
<h2 className="text-slate-300 mt-7">
</motion.section>
{/* CONTRIBUTIONS */}
<motion.h2
className="text-slate-300 mt-7"
variants={fadeInUp}
initial="hidden"
animate="show"
>
{contribs || "Loading.."} contributions in the last year
</h2>
<section className="mt-4">
</motion.h2>
<motion.section
className="mt-4"
initial={{ opacity: 0 }}
whileInView={{ opacity: 1 }}
>
<GitHubCalendar
hideTotalCount
// hideTotalCount
username="selimaj-dev"
theme={{
light: [
@@ -118,16 +193,36 @@ export default function App() {
],
}}
/>
</section>
</motion.section>
{/* FOOTER */}
<footer className="mt-8 border-t pt-3 pb-8">
<p className="flex gap-1 items-center">
If you see this i <HeartIcon className="size-4" /> you
</p>
<p className="text-slate-300 mx-auto w-max mt-2">
<motion.p
className="flex gap-1 items-center"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.4 }}
>
If you see this i{" "}
<motion.span
animate={{ scale: [1, 1.2, 1] }}
transition={{ repeat: Infinity, duration: 1.3 }}
>
<HeartIcon className="size-4" />
</motion.span>{" "}
you
</motion.p>
<motion.p
className="text-slate-300 mx-auto w-max mt-2"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.6 }}
>
© 2025 Klesti. All rights reserved.
</p>
</motion.p>
</footer>
</div>
</div>
</motion.div>
);
}
+17 -19
View File
@@ -1,6 +1,8 @@
"use client";
import { useEffect, useState } from "react";
import { motion } from "framer-motion";
import { fadeInUp, fadeInUp2, staggerContainer } from "./effects";
type PinnedRepo = {
author: string;
@@ -21,22 +23,12 @@ export function Repo({
href,
}: PinnedRepo) {
return (
<div
className={`
group
relative
flex
size-full
overflow-hidden
rounded-xl
h-full
border
border-slate-400
hover:shadow-md
transition-shadow
duration-300
backdrop-blur-[2px]
`}
<motion.div
variants={fadeInUp2}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
className="group relative flex size-full overflow-hidden rounded-xl h-full border border-slate-400 hover:shadow-md transition-shadow duration-300 backdrop-blur-[2px]"
>
<div className="w-full">
<div className="rounded-xl border text-card-foreground shadow h-full bg-transparent border-none flex flex-col">
@@ -133,7 +125,7 @@ export function Repo({
</div>
</div>
</div>
</div>
</motion.div>
);
}
@@ -158,7 +150,13 @@ export default function PinnedRepos() {
});
return (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent">
<motion.div
variants={staggerContainer}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6 bg-transparent"
>
{pinned &&
pinned.map((repo, i) => (
<Repo
@@ -172,7 +170,7 @@ export default function PinnedRepos() {
href={`https://github.com/${repo.author}/${repo.name}`}
/>
))}
</div>
</motion.div>
);
} catch (e: any) {
return <p>{e.toString()}</p>;
+32
View File
@@ -0,0 +1,32 @@
import { easeOut, spring } from "framer-motion";
export const fadeInUp = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};
export const fadeInUp2 = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.4, ease: easeOut },
},
};
export const staggerContainer = {
show: {
transition: {
staggerChildren: 0.12,
},
},
};
export const popIn = {
hidden: { opacity: 0, scale: 0.8 },
show: {
opacity: 1,
scale: 1,
transition: { type: spring, stiffness: 120 },
},
};