feat: add technology and interest icons in About section

This commit is contained in:
2025-02-25 21:53:56 +01:00
parent 6e8a7ecf62
commit 0dd79cd1ec
4 changed files with 152 additions and 34 deletions

View File

@ -11,6 +11,7 @@
"next": "15.1.7",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-icons": "^5.5.0",
"react-intersection-observer": "^9.15.1",
},
"devDependencies": {
@ -641,6 +642,8 @@
"react-dom": ["react-dom@19.0.0", "", { "dependencies": { "scheduler": "^0.25.0" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ=="],
"react-icons": ["react-icons@5.5.0", "", { "peerDependencies": { "react": "*" } }, "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw=="],
"react-intersection-observer": ["react-intersection-observer@9.15.1", "", { "peerDependencies": { "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["react-dom"] }, "sha512-vGrqYEVWXfH+AGu241uzfUpNK4HAdhCkSAyFdkMb9VWWXs6mxzBLpWCxEy9YcnDNY2g9eO6z7qUtTBdA9hc8pA=="],
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],

View File

@ -16,6 +16,7 @@
"next": "15.1.7",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-icons": "^5.5.0",
"react-intersection-observer": "^9.15.1"
},
"devDependencies": {

View File

@ -1,6 +1,35 @@
import { motion } from 'framer-motion';
import Image from 'next/image';
import { FadeIn } from '../ui/FadeIn';
import { FaGithub, FaLinkedin } from 'react-icons/fa';
import { SiPhp, SiJavascript, SiMysql, SiReact, SiNextdotjs, SiTypescript } from 'react-icons/si';
import { MdWeb, MdArchitecture, MdDesignServices, MdSpeed } from 'react-icons/md';
interface Tech {
name: string;
icon: React.ReactNode;
}
interface Interest {
name: string;
icon: React.ReactNode;
}
const technologies: Tech[] = [
{ name: 'PHP', icon: <SiPhp /> },
{ name: 'JavaScript', icon: <SiJavascript /> },
{ name: 'TypeScript', icon: <SiTypescript /> },
{ name: 'React', icon: <SiReact /> },
{ name: 'Next.js', icon: <SiNextdotjs /> },
{ name: 'MySQL', icon: <SiMysql /> }
];
const interests: Interest[] = [
{ name: 'Web Development', icon: <MdWeb /> },
{ name: 'System Architecture', icon: <MdArchitecture /> },
{ name: 'UI/UX Design', icon: <MdDesignServices /> },
{ name: 'Performance Optimization', icon: <MdSpeed /> }
];
export const About = () => {
return (
@ -8,7 +37,9 @@ export const About = () => {
<div className="max-w-screen-xl mx-auto">
<FadeIn>
<div className="flex items-baseline gap-4 mb-12 sm:mb-24">
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight theme-primary">About</h2>
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight theme-primary">
About
</h2>
<div className="h-px flex-grow bg-white/10 relative top-[-4px]"></div>
</div>
</FadeIn>
@ -29,8 +60,12 @@ export const About = () => {
</FadeIn>
<FadeIn delay={0.1}>
<div className="space-y-2 text-center md:text-left">
<h3 className="font-['DM_Sans'] text-xl sm:text-2xl font-medium theme-primary">Jan-Marlon Leibl</h3>
<p className="font-['Instrument_Sans'] theme-text-70 text-base sm:text-lg">Fullstack Developer</p>
<h3 className="font-['DM_Sans'] text-xl sm:text-2xl font-medium theme-primary">
Jan-Marlon Leibl
</h3>
<p className="font-['Instrument_Sans'] theme-text-70 text-base sm:text-lg">
Fullstack Developer
</p>
</div>
</FadeIn>
</div>
@ -49,17 +84,19 @@ export const About = () => {
<div className="grid grid-cols-1 sm:grid-cols-2 gap-10 sm:gap-16">
<div className="space-y-6 sm:space-y-8">
<div className="flex items-baseline gap-4">
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Technologies</h3>
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">
Technologies
</h3>
<div className="h-px flex-grow theme-border"></div>
</div>
<ul className="space-y-4 sm:space-y-5">
{['PHP', 'JavaScript', 'MySQL', 'React'].map((tech) => (
{technologies.map((tech) => (
<li
key={tech}
className="text-base sm:text-lg group flex items-center gap-3 theme-text-70 hover:theme-text-90 transition-colors cursor-default"
key={tech.name}
className="text-base sm:text-lg group flex items-center gap-3 theme-text-70 hover:theme-text-90 transition-colors cursor-default bg-white/[0.02] px-3 py-2 rounded-md"
>
<span className="w-2 h-2 rounded-full theme-text-40 group-hover:theme-text-90 transition-colors"></span>
{tech}
<span className="text-lg theme-text-40 group-hover:theme-text-90 transition-colors">{tech.icon}</span>
{tech.name}
</li>
))}
</ul>
@ -67,22 +104,19 @@ export const About = () => {
<div className="space-y-6 sm:space-y-8">
<div className="flex items-baseline gap-4">
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Interests</h3>
<h3 className="text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">
Interests
</h3>
<div className="h-px flex-grow theme-border"></div>
</div>
<ul className="space-y-4 sm:space-y-5">
{[
'Web Development',
'System Architecture',
'UI/UX Design',
'Performance Optimization'
].map((interest) => (
{interests.map((interest) => (
<li
key={interest}
className="text-base sm:text-lg group flex items-center gap-3 theme-text-70 hover:theme-text-90 transition-colors cursor-default"
key={interest.name}
className="text-base sm:text-lg group flex items-center gap-3 theme-text-70 hover:theme-text-90 transition-colors cursor-default bg-white/[0.02] px-3 py-2 rounded-md"
>
<span className="w-2 h-2 rounded-full theme-text-40 group-hover:theme-text-90 transition-colors"></span>
{interest}
<span className="text-lg theme-text-40 group-hover:theme-text-90 transition-colors">{interest.icon}</span>
{interest.name}
</li>
))}
</ul>
@ -94,18 +128,20 @@ export const About = () => {
href="https://github.com/AtomicWasTaken"
target="_blank"
rel="noopener noreferrer"
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border theme-border rounded-lg sm:rounded-full hover:theme-bg-05 transition-colors text-sm sm:text-base tracking-wide font-medium theme-text-90"
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border theme-border rounded-lg sm:rounded-full hover:theme-bg-05 transition-colors text-sm sm:text-base tracking-wide font-medium theme-text-90 flex items-center justify-center"
whileHover={{ scale: 1.02 }}
>
<FaGithub className="mr-2" />
View GitHub
</motion.a>
<motion.a
href="https://www.linkedin.com/in/janmarlonleibl/"
target="_blank"
rel="noopener noreferrer"
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border theme-border rounded-lg sm:rounded-full hover:theme-bg-05 transition-colors text-sm sm:text-base tracking-wide font-medium theme-text-90"
className="w-full text-center px-6 sm:px-8 py-3 sm:py-4 border theme-border rounded-lg sm:rounded-full hover:theme-bg-05 transition-colors text-sm sm:text-base tracking-wide font-medium theme-text-90 flex items-center justify-center"
whileHover={{ scale: 1.02 }}
>
<FaLinkedin className="mr-2" />
Connect on LinkedIn
</motion.a>
</div>

View File

@ -1,39 +1,109 @@
import { FadeIn } from '../ui/FadeIn';
import { SiNextdotjs, SiTailwindcss, SiTypescript, SiPhp, SiJavascript, SiMysql, SiDiscord } from 'react-icons/si';
import { BsLightningChargeFill } from 'react-icons/bs';
import { FaCode } from 'react-icons/fa';
import { HiOutlineCollection } from 'react-icons/hi';
import { MdLaunch } from 'react-icons/md';
import React from 'react';
interface Project {
title: string;
year: string;
description: string;
tags: string[];
icon: React.ReactNode;
tags: Array<{
name: string;
icon: React.ReactNode;
}>;
}
const getTagIcon = (tag: string) => {
switch (tag) {
case 'Next.js':
return <SiNextdotjs className="text-lg" />;
case 'Tailwind CSS':
return <SiTailwindcss className="text-lg" />;
case 'TypeScript':
return <SiTypescript className="text-lg" />;
case 'PHP':
return <SiPhp className="text-lg" />;
case 'JavaScript':
return <SiJavascript className="text-lg" />;
case 'MySQL':
return <SiMysql className="text-lg" />;
case 'Performance':
return <BsLightningChargeFill className="text-lg" />;
case 'Discord API':
return <SiDiscord className="text-lg" />;
default:
return null;
}
};
const getProjectIcon = (title: string) => {
if (title.includes('ventry')) {
return <MdLaunch className="text-2xl" />;
} else if (title.includes('ShareUpload')) {
return <HiOutlineCollection className="text-2xl" />;
} else if (title.includes('RestoreM')) {
return <SiDiscord className="text-2xl" />;
} else {
return <FaCode className="text-2xl" />;
}
};
const projects: Project[] = [
{
title: 'ventry.host v2',
year: '2025',
description: 'Free file hosting revamped with a modern design and improved user experience.',
tags: ['Next.js', 'Tailwind CSS', 'TypeScript']
icon: getProjectIcon('ventry.host v2'),
tags: [
{ name: 'Next.js', icon: getTagIcon('Next.js') },
{ name: 'Tailwind CSS', icon: getTagIcon('Tailwind CSS') },
{ name: 'TypeScript', icon: getTagIcon('TypeScript') }
]
},
{
title: 'ventry.host',
year: '2023',
description: 'A free file hosting solution with thousands of daily visitors.',
tags: ['PHP', 'JavaScript', 'MySQL']
icon: getProjectIcon('ventry.host'),
tags: [
{ name: 'PHP', icon: getTagIcon('PHP') },
{ name: 'JavaScript', icon: getTagIcon('JavaScript') },
{ name: 'MySQL', icon: getTagIcon('MySQL') }
]
},
{
title: 'ShareUpload',
year: '2022',
description: 'High-performance file sharing platform with unlimited storage.',
tags: ['PHP', 'MySQL', 'Performance']
icon: getProjectIcon('ShareUpload'),
tags: [
{ name: 'PHP', icon: getTagIcon('PHP') },
{ name: 'MySQL', icon: getTagIcon('MySQL') },
{ name: 'Performance', icon: getTagIcon('Performance') }
]
},
{
title: 'RestoreM',
year: '2023',
description: 'Discord server backup and restoration service.',
tags: ['PHP', 'MySQL', 'Discord API']
icon: getProjectIcon('RestoreM'),
tags: [
{ name: 'PHP', icon: getTagIcon('PHP') },
{ name: 'MySQL', icon: getTagIcon('MySQL') },
{ name: 'Discord API', icon: getTagIcon('Discord API') }
]
}
];
// Sort projects by year (newest first)
const sortedProjects = [...projects].sort((a, b) => {
return parseInt(b.year) - parseInt(a.year);
});
export const Work = () => {
return (
<section id="work" className="py-20 sm:py-40 px-4 sm:px-8 relative">
@ -41,16 +111,20 @@ export const Work = () => {
<div className="max-w-screen-xl mx-auto relative">
<FadeIn>
<div className="flex flex-col gap-3 mb-12 sm:mb-24">
<span className="theme-text-40 uppercase tracking-[0.2em] text-sm sm:text-base font-['Instrument_Sans']">Portfolio</span>
<span className="theme-text-40 uppercase tracking-[0.2em] text-sm sm:text-base font-['Instrument_Sans']">
Portfolio
</span>
<div className="flex items-baseline gap-4">
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight theme-primary">Selected Work</h2>
<h2 className="font-['DM_Sans'] text-3xl sm:text-6xl font-semibold tracking-tight theme-primary">
Selected Work
</h2>
<div className="h-px flex-grow theme-border"></div>
</div>
</div>
</FadeIn>
<div className="grid gap-24 sm:gap-40">
{projects.map((project, index) => (
{sortedProjects.map((project, index) => (
<FadeIn key={index} delay={index * 0.1}>
<div className="group relative">
<div className="absolute top-0 left-0 right-0 flex items-center gap-4">
@ -72,15 +146,19 @@ export const Work = () => {
<div className="space-y-6 sm:space-y-12">
<div className="space-y-4 sm:space-y-6">
<h4 className="font-['Instrument_Sans'] text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">Technologies</h4>
<h4 className="font-['Instrument_Sans'] text-sm sm:text-base theme-text-40 uppercase tracking-[0.2em]">
Technologies
</h4>
<div className="flex flex-wrap items-center gap-3 sm:gap-4">
{project.tags.map((tag, tagIndex) => (
<span
key={tagIndex}
className="text-sm sm:text-base theme-text-70 font-['Instrument_Sans'] tracking-wide py-1 sm:py-2 group-hover:theme-text-90 transition-colors"
className="flex items-center text-sm sm:text-base theme-text-70 font-['Instrument_Sans'] tracking-wide py-1 sm:py-2 group-hover:theme-text-90 transition-colors bg-white/[0.02] px-3 sm:px-4 rounded-full shadow-sm"
>
{tag}{tagIndex !== project.tags.length - 1 && (
<span className="mx-3 sm:mx-4 theme-text-40 select-none"></span>
<span className="mr-2 flex items-center">{tag.icon}</span>
{tag.name}
{tagIndex !== project.tags.length - 1 && (
<span className="ml-2 opacity-0"></span>
)}
</span>
))}