feat: update personal information and localization in various components
Deploy Portfolio Selfmade / deploy (push) Failing after 15s

This commit is contained in:
2026-05-10 14:52:21 +02:00
parent 4ff07915b8
commit 430e02c8ce
14 changed files with 337 additions and 186 deletions
@@ -118,7 +118,7 @@ export const ContactSection: React.FC = () => {
</div> </div>
<div> <div>
<p className="text-gray-400 text-sm">Email</p> <p className="text-gray-400 text-sm">Email</p>
<p className="text-white font-medium">alex@himetoprogram.com</p> <p className="text-white font-medium">robert@bretz.dev</p>
</div> </div>
</div> </div>
@@ -128,7 +128,7 @@ export const ContactSection: React.FC = () => {
</div> </div>
<div> <div>
<p className="text-gray-400 text-sm">Location</p> <p className="text-gray-400 text-sm">Location</p>
<p className="text-white font-medium">San Francisco, CA</p> <p className="text-white font-medium">Germany</p>
</div> </div>
</div> </div>
</div> </div>
@@ -3,40 +3,40 @@ import type { Experience } from '../../../shared/types';
export const experienceData: Experience[] = [ export const experienceData: Experience[] = [
{ {
id: '1', id: '1',
position: 'Senior React Developer', position: 'Senior Software Engineer',
company: 'Tech Startup Inc.', company: 'Enterprise Systems Group',
startYear: 2023, startYear: 2024,
description: description:
'Led the development of multiple high-performance React applications, mentored junior developers, and implemented modern design systems', 'Designed scalable backend systems with .NET, SQL, Docker and advanced architecture patterns for enterprise workflows.',
technologies: ['React', 'TypeScript', 'Next.js', 'GraphQL'], technologies: ['.NET', 'SQL', 'Docker', 'Clean Architecture'],
}, },
{ {
id: '2', id: '2',
position: 'Full Stack Developer', position: 'Full Stack Architect',
company: 'Digital Agency Co.', company: 'Innovative SaaS Labs',
startYear: 2022, startYear: 2022,
endYear: 2023, endYear: 2024,
description: description:
'Developed responsive web applications for clients, collaborated with designers, and optimized performance metrics', 'Owned end-to-end product architecture using React, TypeScript, NestJS and Vertical Slice Architecture for high-performance applications.',
technologies: ['React', 'Node.js', 'MongoDB', 'Tailwind CSS'], technologies: ['React', 'NestJS', 'TypeScript', 'Vertical Slice Architecture'],
}, },
{ {
id: '3', id: '3',
position: 'Junior Frontend Developer', position: 'Backend Developer',
company: 'Web Solutions Ltd.', company: 'Platform Engineering Co.',
startYear: 2021, startYear: 2020,
endYear: 2022, endYear: 2022,
description: description:
'Built responsive web interfaces, fixed bugs, and participated in code reviews to improve code quality', 'Built resilient APIs and development workflows with Express.js, Fastify, Node.js and Linux deployment automation.',
technologies: ['React', 'JavaScript', 'CSS', 'HTML'], technologies: ['Express.js', 'Fastify', 'Node.js', 'Linux'],
}, },
{ {
id: '4', id: '4',
position: 'Freelance Web Developer', position: 'Creative Tech Consultant',
startYear: 2020, startYear: 2018,
endYear: 2021, endYear: 2020,
description: description:
'Created custom websites for various clients, managed projects independently, and delivered quality work on time', 'Delivered immersive digital experiences using Unity, Blender, Godot and LaTeX documentation for creative product storytelling.',
technologies: ['React', 'WordPress', 'JavaScript', 'jQuery'], technologies: ['Unity', 'Blender', 'Godot', 'LaTeX'],
}, },
]; ];
@@ -46,17 +46,17 @@ export const HeroSection: React.FC = () => {
<div className="inline-block px-4 py-2 border border-green-500/50 rounded-full bg-green-500/10 backdrop-blur-sm"> <div className="inline-block px-4 py-2 border border-green-500/50 rounded-full bg-green-500/10 backdrop-blur-sm">
<span className="text-green-400 text-sm font-medium flex items-center gap-2"> <span className="text-green-400 text-sm font-medium flex items-center gap-2">
<span className="w-2 h-2 bg-green-500 rounded-full"></span> <span className="w-2 h-2 bg-green-500 rounded-full"></span>
React Developer & UI/UX Enthusiast | Based in San Francisco, CA Softwareentwickler & Architektur-Enthusiast | Deutschland
</span> </span>
</div> </div>
</motion.div> </motion.div>
{/* Main Heading */} {/* Main Heading */}
<motion.h1 variants={itemVariants} className="text-5xl sm:text-6xl lg:text-7xl font-bold mb-6"> <motion.h1 variants={itemVariants} className="text-5xl sm:text-6xl lg:text-7xl font-bold mb-6 leading-tight">
<span className="text-white">React is Developer</span> <span className="text-white">Robert Bretz</span>
<br /> <br />
<span className="bg-gradient-to-r from-green-500 to-emerald-500 bg-clip-text text-transparent"> <span className="bg-gradient-to-r from-green-500 to-emerald-500 bg-clip-text text-transparent">
Portfolio Crafting clean, performant digital products
</span> </span>
</motion.h1> </motion.h1>
@@ -65,44 +65,44 @@ export const HeroSection: React.FC = () => {
variants={itemVariants} variants={itemVariants}
className="text-gray-300 text-lg sm:text-xl max-w-2xl mx-auto mb-8" className="text-gray-300 text-lg sm:text-xl max-w-2xl mx-auto mb-8"
> >
Building modern, scalable web applications with React, JavaScript, and cutting-edge technologies. Ich entwickle moderne Anwendungen mit React, TypeScript, Tailwind, .NET, NestJS und sauberer Architektur.
Transforming ideas into exceptional digital experiences. Mein Fokus liegt auf Performance, Vertical Slice Architecture, Docker-Deployments und skalierbaren Backend-Systemen.
</motion.p> </motion.p>
{/* Stats */} {/* Stats */}
<motion.div <motion.div
variants={itemVariants} variants={itemVariants}
className="grid grid-cols-3 gap-4 sm:gap-8 mb-12 max-w-2xl mx-auto" className="grid grid-cols-1 sm:grid-cols-3 gap-4 sm:gap-8 mb-12 max-w-2xl mx-auto"
> >
<div className="border border-green-500/30 rounded-lg p-4 bg-green-500/5"> <div className="border border-green-500/30 rounded-lg p-5 bg-green-500/5 backdrop-blur-sm">
<div className="text-2xl sm:text-3xl font-bold text-green-500">3+</div> <div className="text-3xl font-bold text-green-500">4+</div>
<div className="text-gray-400 text-sm">Years Experience</div> <div className="text-gray-400 text-sm">Years Engineering</div>
</div> </div>
<div className="border border-green-500/30 rounded-lg p-4 bg-green-500/5"> <div className="border border-green-500/30 rounded-lg p-5 bg-green-500/5 backdrop-blur-sm">
<div className="text-2xl sm:text-3xl font-bold text-green-500">50+</div> <div className="text-3xl font-bold text-green-500">30+</div>
<div className="text-gray-400 text-sm">Projects Completed</div> <div className="text-gray-400 text-sm">Delivered Solutions</div>
</div> </div>
<div className="border border-green-500/30 rounded-lg p-4 bg-green-500/5"> <div className="border border-green-500/30 rounded-lg p-5 bg-green-500/5 backdrop-blur-sm">
<div className="text-2xl sm:text-3xl font-bold text-green-500">98%</div> <div className="text-3xl font-bold text-green-500">99%</div>
<div className="text-gray-400 text-sm">Client Satisfaction</div> <div className="text-gray-400 text-sm">Performance Focus</div>
</div> </div>
</motion.div> </motion.div>
{/* CTA Buttons */} {/* CTA Buttons */}
<motion.div variants={itemVariants} className="flex flex-col sm:flex-row gap-4 justify-center mb-12"> <motion.div variants={itemVariants} className="flex flex-col sm:flex-row gap-4 justify-center mb-12">
<Button variant="primary" size="lg"> <Button variant="primary" size="lg">
Get in Touch Lass uns sprechen
</Button> </Button>
<Button variant="secondary" size="lg"> <Button variant="secondary" size="lg">
View My Work Projekte ansehen
</Button> </Button>
</motion.div> </motion.div>
{/* Scroll Indicator */} {/* Scroll Indicator */}
<motion.div <motion.div
variants={itemVariants} variants={itemVariants}
animate={{ y: [0, 10, 0] }} animate={{ y: [0, 12, 0] }}
transition={{ duration: 2, repeat: Infinity }} transition={{ duration: 2, repeat: Infinity, ease: 'easeInOut' }}
className="flex justify-center" className="flex justify-center"
> >
<ArrowDown className="text-green-500" size={32} /> <ArrowDown className="text-green-500" size={32} />
@@ -1,49 +1,54 @@
import React from 'react'; import React, { useState } from 'react';
import { motion } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { Menu, X } from 'lucide-react';
import { useNavigation } from '../hooks/useNavigation'; import { useNavigation } from '../hooks/useNavigation';
import { useScrollToSection } from '../../../shared/hooks/useScrollToSection'; import { useScrollToSection } from '../../../shared/hooks/useScrollToSection';
import Button from '../../../shared/components/Button'; import Button from '../../../shared/components/Button';
const navItems = [ const navItems = [
{ id: 'home', label: 'About' }, { id: 'home', label: 'Über mich' },
{ id: 'skills', label: 'Skills' }, { id: 'skills', label: 'Fähigkeiten' },
{ id: 'projects', label: 'Projects' }, { id: 'projects', label: 'Projekte' },
{ id: 'experience', label: 'Experience' }, { id: 'experience', label: 'Erfahrung' },
{ id: 'contact', label: 'Contact' }, { id: 'contact', label: 'Kontakt' },
]; ];
export const Navigation: React.FC = () => { export const Navigation: React.FC = () => {
const [isOpen, setIsOpen] = useState(false);
const { activeSection, isScrolled } = useNavigation(); const { activeSection, isScrolled } = useNavigation();
const { scrollToSection } = useScrollToSection(); const { scrollToSection } = useScrollToSection();
const handleNavClick = (id: string) => {
setIsOpen(false);
scrollToSection(id);
};
return ( return (
<motion.nav <motion.nav
initial={{ y: -100 }} initial={{ y: -120 }}
animate={{ y: 0 }} animate={{ y: 0 }}
transition={{ duration: 0.5 }} transition={{ duration: 0.45, ease: 'easeOut' }}
className={`fixed top-0 w-full z-50 transition-all duration-300 ${ className={`fixed top-0 w-full z-50 transition-all duration-300 ${
isScrolled isScrolled
? 'bg-black/80 backdrop-blur-md border-b border-green-500/20' ? 'bg-black/90 backdrop-blur-xl border-b border-green-500/20 shadow-lg shadow-emerald-500/5'
: 'bg-transparent' : 'bg-transparent'
}`} }`}
> >
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"> <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16"> <div className="flex justify-between items-center h-16">
{/* Logo */}
<motion.div <motion.div
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
className="text-2xl font-bold text-green-500 cursor-pointer" className="text-2xl font-bold text-green-500 cursor-pointer"
onClick={() => scrollToSection('home')} onClick={() => handleNavClick('home')}
> >
&lt;/&gt; Alex &lt;/&gt; Robert Bretz
</motion.div> </motion.div>
{/* Nav Items */}
<div className="hidden md:flex gap-8"> <div className="hidden md:flex gap-8">
{navItems.map((item) => ( {navItems.map((item) => (
<motion.button <motion.button
key={item.id} key={item.id}
onClick={() => scrollToSection(item.id)} onClick={() => handleNavClick(item.id)}
className={`text-sm font-medium transition-colors duration-300 pb-2 border-b-2 ${ className={`text-sm font-medium transition-colors duration-300 pb-2 border-b-2 ${
activeSection === item.id activeSection === item.id
? 'text-green-500 border-green-500' ? 'text-green-500 border-green-500'
@@ -57,12 +62,51 @@ export const Navigation: React.FC = () => {
))} ))}
</div> </div>
{/* CTA Button */} <div className="flex items-center gap-3">
<div className="hidden md:block">
<Button variant="primary" size="sm"> <Button variant="primary" size="sm">
Hire Me Anfrage
</Button> </Button>
</div> </div>
<button
type="button"
aria-label="Open mobile menu"
className="md:hidden p-2 rounded-full border border-green-500/20 bg-green-500/10 text-green-400 hover:text-white hover:border-green-500 transition-all"
onClick={() => setIsOpen((prev) => !prev)}
>
{isOpen ? <X size={22} /> : <Menu size={22} />}
</button>
</div> </div>
</div>
</div>
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0, y: -20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
className="md:hidden bg-black/95 border-t border-green-500/20"
>
<div className="px-6 py-6 space-y-4">
{navItems.map((item) => (
<button
key={item.id}
onClick={() => handleNavClick(item.id)}
className={`w-full text-left text-lg font-medium px-4 py-3 rounded-xl transition-colors duration-300 ${
activeSection === item.id
? 'bg-green-500/15 text-green-400'
: 'text-gray-300 hover:bg-white/5 hover:text-white'
}`}
>
{item.label}
</button>
))}
</div>
</motion.div>
)}
</AnimatePresence>
</motion.nav> </motion.nav>
); );
}; };
@@ -12,58 +12,58 @@ interface ProjectCardProps {
export const ProjectCard: React.FC<ProjectCardProps> = ({ project, index }) => { export const ProjectCard: React.FC<ProjectCardProps> = ({ project, index }) => {
return ( return (
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} layout
initial={{ opacity: 0, y: 22 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.45, delay: index * 0.08, ease: 'easeOut' }}
whileHover={{ y: -10 }} whileHover={{ y: -8, scale: 1.01 }}
className="group rounded-xl overflow-hidden border border-green-500/30 bg-gradient-to-br from-green-500/5 to-emerald-500/5 backdrop-blur-sm hover:border-green-500/60 transition-all duration-300" className="group rounded-3xl overflow-hidden border border-green-500/25 bg-gradient-to-br from-green-500/5 to-emerald-500/5 backdrop-blur-sm hover:border-green-500/55 transition-all duration-300 shadow-[0_20px_60px_-40px_rgba(34,197,94,0.8)]"
> >
{/* Image */} {/* Image */}
<div className="relative h-64 overflow-hidden bg-gradient-to-br from-gray-900 to-black"> <div className="relative h-64 overflow-hidden bg-gradient-to-br from-gray-900 to-black">
<img <img
src={project.image} src={project.image}
alt={project.title} alt={project.title}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500" className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500 ease-out"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/80 to-transparent"></div> <div className="absolute inset-0 bg-gradient-to-t from-black/85 to-transparent"></div>
</div> </div>
{/* Content */} {/* Content */}
<div className="p-6"> <div className="p-6">
{/* Category Badge */} <div className="inline-flex items-center gap-2 mb-4 px-3 py-1 rounded-full bg-green-500/15 border border-green-500/25">
<div className="inline-block mb-4 px-3 py-1 rounded-full bg-green-500/20 border border-green-500/50">
<span className="text-green-400 text-xs font-semibold">{project.category}</span> <span className="text-green-400 text-xs font-semibold">{project.category}</span>
</div> </div>
<h3 className="text-xl font-bold text-white mb-2">{project.title}</h3> <h3 className="text-2xl font-bold text-white mb-3">{project.title}</h3>
<p className="text-gray-400 text-sm mb-4 line-clamp-2">{project.description}</p> <p className="text-gray-400 text-sm mb-5 line-clamp-3">{project.description}</p>
{/* Tags */}
<div className="flex flex-wrap gap-2 mb-6"> <div className="flex flex-wrap gap-2 mb-6">
{project.tags.slice(0, 3).map((tag) => ( {project.tags.slice(0, 3).map((tag) => (
<span <span
key={tag} key={tag}
className="text-xs px-2 py-1 rounded border border-green-500/30 text-green-400" className="text-xs px-2 py-1 rounded-full border border-green-500/25 text-green-300 bg-white/5"
> >
{tag} {tag}
</span> </span>
))} ))}
{project.tags.length > 3 && ( {project.tags.length > 3 && (
<span className="text-xs px-2 py-1 text-gray-500"> <span className="text-xs px-2 py-1 rounded-full text-gray-400 bg-gray-900/40">
+{project.tags.length - 3} +{project.tags.length - 3}
</span> </span>
)} )}
</div> </div>
{/* Links */} <div className="flex flex-wrap gap-3">
<div className="flex gap-3">
{project.link && ( {project.link && (
<motion.a <motion.a
href={project.link} href={project.link}
whileHover={{ scale: 1.1 }} target="_blank"
rel="noreferrer"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-green-500/20 hover:bg-green-500/30 text-green-400 transition-colors text-sm font-medium" className="inline-flex items-center gap-2 px-4 py-2 rounded-2xl bg-green-500/20 hover:bg-green-500/30 text-green-300 transition-colors text-sm font-medium"
> >
<ExternalLink size={16} /> <ExternalLink size={16} />
Live Live
@@ -72,9 +72,11 @@ export const ProjectCard: React.FC<ProjectCardProps> = ({ project, index }) => {
{project.github && ( {project.github && (
<motion.a <motion.a
href={project.github} href={project.github}
whileHover={{ scale: 1.1 }} target="_blank"
rel="noreferrer"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
className="flex items-center gap-2 px-4 py-2 rounded-lg border border-green-500/30 hover:border-green-500/60 text-gray-300 hover:text-white transition-colors text-sm font-medium" className="inline-flex items-center gap-2 px-4 py-2 rounded-2xl border border-green-500/25 hover:border-green-500/50 text-gray-300 hover:text-white transition-colors text-sm font-medium"
> >
<FaGithub size={16} /> <FaGithub size={16} />
Code Code
@@ -5,12 +5,12 @@ import { ProjectCard } from './ProjectCard';
import AnimatedSection from '../../../shared/components/AnimatedSection'; import AnimatedSection from '../../../shared/components/AnimatedSection';
import type { Project } from '../../../shared/types'; import type { Project } from '../../../shared/types';
type Category = 'All' | 'Web Apps' | 'UI Components' | 'Full Stack'; type Category = 'Alle' | 'Webanwendungen' | 'UI-Komponenten' | 'Fullstack';
export const ProjectsSection: React.FC = () => { export const ProjectsSection: React.FC = () => {
const [selectedCategory, setSelectedCategory] = useState<Category>('All'); const [selectedCategory, setSelectedCategory] = useState<Category>('Alle');
const categories: Category[] = ['All', 'Web Apps', 'UI Components', 'Full Stack']; const categories: Category[] = ['Alle', 'Webanwendungen', 'UI-Komponenten', 'Fullstack'];
const filteredProjects = projectsData.filter( const filteredProjects = projectsData.filter(
(project: Project) => (project: Project) =>
@@ -29,14 +29,14 @@ export const ProjectsSection: React.FC = () => {
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
className="inline-block px-4 py-2 rounded-full border border-green-500/50 bg-green-500/10 mb-6" className="inline-block px-4 py-2 rounded-full border border-green-500/50 bg-green-500/10 mb-6"
> >
<span className="text-green-400 text-sm font-medium">My Work</span> <span className="text-green-400 text-sm font-medium">Meine Projekte</span>
</motion.div> </motion.div>
<h2 className="text-4xl sm:text-5xl font-bold text-white mb-4"> <h2 className="text-4xl sm:text-5xl font-bold text-white mb-4">
Featured Projects Ausgewählte Projekte
</h2> </h2>
<p className="text-gray-400 text-lg max-w-2xl mx-auto"> <p className="text-gray-400 text-lg max-w-2xl mx-auto">
Showcasing my best work and achievements Eine Auswahl meiner besten Lösungen und Erfolge
</p> </p>
</div> </div>
@@ -3,34 +3,34 @@ import type { Project } from '../../../shared/types';
export const projectsData: Project[] = [ export const projectsData: Project[] = [
{ {
id: '1', id: '1',
title: 'E-Commerce Platform', title: 'Order Flow Microservice Suite',
description: description:
'Full-stack e-commerce platform with product catalog, shopping cart, and payment integration using Stripe', 'Skalierbare Auftragsverwaltung mit .NET, SQL, Docker und SQS für robuste, ereignisgesteuerte Commerce-Flows.',
image: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=800&h=600&fit=crop', image: 'https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=800&h=600&fit=crop',
tags: ['React', 'Next.js', 'TypeScript', 'Tailwind CSS', 'MongoDB'], tags: ['.NET', 'Docker', 'SQL', 'SQS', 'Clean Architecture'],
category: 'Full Stack', category: 'Fullstack',
link: '#', link: '#',
github: '#', github: '#',
}, },
{ {
id: '2', id: '2',
title: 'Task Management Dashboard', title: 'Design System Engine',
description: description:
'A collaborative task management application with real-time updates, user authentication, and advanced filtering', 'Wiederverwendbares UI-System mit React, Tailwind, Figma-Tokens und Storybook für teamübergreifende Konsistenz.',
image: 'https://images.unsplash.com/photo-1552664730-d307ca884978?w=800&h=600&fit=crop', image: 'https://images.unsplash.com/photo-1519389950473-47ba0277781c?w=800&h=600&fit=crop',
tags: ['React', 'Firebase', 'TypeScript', 'Tailwind CSS'], tags: ['React', 'Tailwind CSS', 'Figma', 'Storybook'],
category: 'Web Apps', category: 'UI-Komponenten',
link: '#', link: '#',
github: '#', github: '#',
}, },
{ {
id: '3', id: '3',
title: 'Component Library', title: 'Performance Platform',
description: description:
'A comprehensive reusable component library with Storybook documentation and design system tokens', 'Leistungsstarkes Backend mit Node.js, Fastify, NestJS und Docker, optimiert für schnelle Deployments und Observability.',
image: 'https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=800&h=600&fit=crop', image: 'https://images.unsplash.com/photo-1504384308090-c894fdcc538d?w=800&h=600&fit=crop',
tags: ['React', 'Storybook', 'Tailwind CSS', 'TypeScript'], tags: ['Node.js', 'Fastify', 'NestJS', 'Docker', 'Linux'],
category: 'UI Components', category: 'Webanwendungen',
link: '#', link: '#',
github: '#', github: '#',
}, },
@@ -4,7 +4,7 @@ import { iconMap } from '../data/skillsData';
import type { Skill } from '../../../shared/types'; import type { Skill } from '../../../shared/types';
interface SkillCardProps { interface SkillCardProps {
skill: Skill; skill: Skill & { iconName: keyof typeof iconMap };
index: number; index: number;
} }
@@ -23,44 +23,41 @@ export const SkillCard: React.FC<SkillCardProps> = ({ skill, index }) => {
Beginner: 'bg-blue-500/20', Beginner: 'bg-blue-500/20',
}; };
// Get icon from the mapped skill name const IconComponent = iconMap[skill.iconName];
const IconComponent = iconMap[skill.name as keyof typeof iconMap];
return ( return (
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} layout
initial={{ opacity: 0, y: 24 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.45, delay: index * 0.08, ease: 'easeOut' }}
whileHover={{ scale: 1.05, y: -10 }} whileHover={{ scale: 1.025, y: -6 }}
className="p-6 rounded-xl border border-green-500/30 bg-gradient-to-br from-green-500/5 to-emerald-500/5 backdrop-blur-sm hover:border-green-500/60 transition-all duration-300" className="p-6 rounded-3xl border border-green-500/25 bg-gradient-to-br from-green-500/5 to-emerald-500/5 backdrop-blur-sm hover:border-green-500/60 transition-all duration-300 shadow-[0_20px_60px_-40px_rgba(34,197,94,0.75)]"
> >
<div className="flex items-start justify-between mb-4"> <div className="flex items-start justify-between mb-4">
<div className="p-3 rounded-lg bg-green-500/20"> <div className="p-3 rounded-2xl bg-green-500/15">
{IconComponent && <IconComponent className="text-green-500" size={24} />} {IconComponent && <IconComponent className="text-green-500" size={24} />}
</div> </div>
<span <span className={`text-xs font-semibold px-3 py-1 rounded-full ${levelBg[skill.level]}`}>
className={`text-xs font-semibold px-3 py-1 rounded-full ${levelBg[skill.level]}`}
>
<span className={levelColors[skill.level]}>{skill.level}</span> <span className={levelColors[skill.level]}>{skill.level}</span>
</span> </span>
</div> </div>
<h3 className="text-lg font-bold text-white mb-2">{skill.name}</h3> <h3 className="text-lg font-bold text-white mb-3">{skill.name}</h3>
<div className="space-y-3"> <div className="space-y-3">
{/* Progress Bar */}
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<span className="text-gray-400">Proficiency</span> <span className="text-gray-400">Experience</span>
<span className="text-green-400 font-medium">{skill.years}+ yrs</span> <span className="text-green-400 font-medium">{skill.years}+ yrs</span>
</div> </div>
<div className="h-2 bg-gray-700 rounded-full overflow-hidden"> <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
<motion.div <motion.div
initial={{ width: 0 }} initial={{ width: 0 }}
whileInView={{ width: `${(skill.years / 5) * 100}%` }} whileInView={{ width: `${Math.min((skill.years / 5) * 100, 100)}%` }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 1, delay: index * 0.1 + 0.3 }} transition={{ duration: 0.9, delay: index * 0.1 + 0.2 }}
className="h-full bg-gradient-to-r from-green-500 to-emerald-500" className="h-full bg-gradient-to-r from-green-500 to-emerald-500"
/> />
</div> </div>
@@ -4,12 +4,27 @@ import { skillsData } from '../data/skillsData';
import { SkillCard } from './SkillCard'; import { SkillCard } from './SkillCard';
import AnimatedSection from '../../../shared/components/AnimatedSection'; import AnimatedSection from '../../../shared/components/AnimatedSection';
type Category = 'All' | 'Frontend' | 'Backend' | 'Tools' | 'Design'; type Category =
| 'Alle'
| 'Frontend'
| 'Backend'
| 'Tools'
| 'Design'
| 'Architektur'
| 'Kreativ';
export const SkillsSection: React.FC = () => { export const SkillsSection: React.FC = () => {
const [selectedCategory, setSelectedCategory] = useState<Category>('All'); const [selectedCategory, setSelectedCategory] = useState<Category>('Alle');
const categories: Category[] = ['All', 'Frontend', 'Backend', 'Tools', 'Design']; const categories: Category[] = [
'Alle',
'Frontend',
'Backend',
'Tools',
'Design',
'Architektur',
'Kreativ',
];
const filteredSkills = skillsData.filter( const filteredSkills = skillsData.filter(
(skill) => selectedCategory === 'All' || skill.category === selectedCategory (skill) => selectedCategory === 'All' || skill.category === selectedCategory
@@ -27,14 +42,14 @@ export const SkillsSection: React.FC = () => {
transition={{ duration: 0.5 }} transition={{ duration: 0.5 }}
className="inline-block px-4 py-2 rounded-full border border-green-500/50 bg-green-500/10 mb-6" className="inline-block px-4 py-2 rounded-full border border-green-500/50 bg-green-500/10 mb-6"
> >
<span className="text-green-400 text-sm font-medium">My Expertise</span> <span className="text-green-400 text-sm font-medium">Meine Expertise</span>
</motion.div> </motion.div>
<h2 className="text-4xl sm:text-5xl font-bold text-white mb-4"> <h2 className="text-4xl sm:text-5xl font-bold text-white mb-4">
Skills & Technologies Fähigkeiten & Technologien
</h2> </h2>
<p className="text-gray-400 text-lg max-w-2xl mx-auto"> <p className="text-gray-400 text-lg max-w-2xl mx-auto">
A comprehensive overview of my technical skills and proficiency levels Ein Überblick über meine technischen Fähigkeiten und Erfahrungsstufen.
</p> </p>
</div> </div>
+146 -56
View File
@@ -6,20 +6,40 @@ import {
GitBranch, GitBranch,
Wind, Wind,
Package, Package,
PenTool,
Layers,
Terminal,
Box,
FileText,
Gamepad,
ShieldCheck,
} from 'lucide-react'; } from 'lucide-react';
import type { Skill } from '../../../shared/types'; import type { Skill } from '../../../shared/types';
export const iconMap = { export const iconMap = {
'React.js': Code, 'React.js': Code,
'Vue.js': Code,
'JavaScript': Zap, 'JavaScript': Zap,
'TypeScript': Code, 'TypeScript': Code,
'Next.js': Package,
'Node.js': Database,
'REST APIs': Code,
'Git & GitHub': GitBranch,
'Responsive Design': Layout,
'Figma': Wind,
'Tailwind CSS': Wind, 'Tailwind CSS': Wind,
'CSS': Layout,
'.NET': ShieldCheck,
'Node.js': Database,
'Express.js': Zap,
'Fastify': Box,
'NestJS': Layers,
'SQL / SQS': Database,
'Git & GitHub': GitBranch,
'Docker': Box,
'Linux': Terminal,
'Clean Architecture': Layers,
'Vertical Slice Architecture': Layers,
'Design Patterns': Layout,
'Figma': PenTool,
'Unity': Gamepad,
'Blender': Package,
'Godot': Package,
'LaTeX': FileText,
}; };
export const skillsData: Array<Omit<Skill, 'icon'> & { iconName: keyof typeof iconMap }> = [ export const skillsData: Array<Omit<Skill, 'icon'> & { iconName: keyof typeof iconMap }> = [
@@ -27,70 +47,140 @@ export const skillsData: Array<Omit<Skill, 'icon'> & { iconName: keyof typeof ic
name: 'React.js', name: 'React.js',
iconName: 'React.js', iconName: 'React.js',
level: 'Expert', level: 'Expert',
years: 3, years: 4,
category: 'Frontend', category: 'Frontend',
}, },
{ {
name: 'JavaScript', name: 'Vue.js',
iconName: 'JavaScript', iconName: 'Vue.js',
level: 'Expert', level: 'Advanced',
years: 4, years: 3,
category: 'Frontend', category: 'Frontend',
}, },
{ {
name: 'TypeScript', name: 'TypeScript',
iconName: 'TypeScript', iconName: 'TypeScript',
level: 'Advanced',
years: 2,
category: 'Frontend',
},
{
name: 'Next.js',
iconName: 'Next.js',
level: 'Advanced',
years: 2,
category: 'Frontend',
},
{
name: 'Node.js',
iconName: 'Node.js',
level: 'Intermediate',
years: 2,
category: 'Backend',
},
{
name: 'REST APIs',
iconName: 'REST APIs',
level: 'Advanced',
years: 3,
category: 'Backend',
},
{
name: 'Git & GitHub',
iconName: 'Git & GitHub',
level: 'Advanced',
years: 4,
category: 'Tools',
},
{
name: 'Responsive Design',
iconName: 'Responsive Design',
level: 'Expert', level: 'Expert',
years: 3, years: 4,
category: 'Design', category: 'Frontend',
},
{
name: 'Figma',
iconName: 'Figma',
level: 'Intermediate',
years: 2,
category: 'Design',
}, },
{ {
name: 'Tailwind CSS', name: 'Tailwind CSS',
iconName: 'Tailwind CSS', iconName: 'Tailwind CSS',
level: 'Expert', level: 'Expert',
years: 3, years: 4,
category: 'Frontend', category: 'Frontend',
}, },
{
name: '.NET',
iconName: '.NET',
level: 'Advanced',
years: 3,
category: 'Backend',
},
{
name: 'Express.js',
iconName: 'Express.js',
level: 'Advanced',
years: 3,
category: 'Backend',
},
{
name: 'Fastify',
iconName: 'Fastify',
level: 'Advanced',
years: 2,
category: 'Backend',
},
{
name: 'NestJS',
iconName: 'NestJS',
level: 'Advanced',
years: 2,
category: 'Backend',
},
{
name: 'SQL / SQS',
iconName: 'SQL / SQS',
level: 'Advanced',
years: 3,
category: 'Backend',
},
{
name: 'Clean Architecture',
iconName: 'Clean Architecture',
level: 'Advanced',
years: 3,
category: 'Architektur',
},
{
name: 'Vertical Slice Architecture',
iconName: 'Vertical Slice Architecture',
level: 'Advanced',
years: 3,
category: 'Architektur',
},
{
name: 'Design Patterns',
iconName: 'Design Patterns',
level: 'Advanced',
years: 4,
category: 'Architektur',
},
{
name: 'Docker',
iconName: 'Docker',
level: 'Advanced',
years: 4,
category: 'Tools',
},
{
name: 'Git & GitHub',
iconName: 'Git & GitHub',
level: 'Expert',
years: 5,
category: 'Tools',
},
{
name: 'Linux',
iconName: 'Linux',
level: 'Advanced',
years: 4,
category: 'Tools',
},
{
name: 'Figma',
iconName: 'Figma',
level: 'Advanced',
years: 3,
category: 'Design',
},
{
name: 'Unity',
iconName: 'Unity',
level: 'Intermediate',
years: 2,
category: 'Kreativ',
},
{
name: 'Blender',
iconName: 'Blender',
level: 'Intermediate',
years: 2,
category: 'Kreativ',
},
{
name: 'Godot',
iconName: 'Godot',
level: 'Intermediate',
years: 2,
category: 'Kreativ',
},
{
name: 'LaTeX',
iconName: 'LaTeX',
level: 'Intermediate',
years: 3,
category: 'Kreativ',
},
]; ];
+1
View File
@@ -13,6 +13,7 @@
html { html {
scroll-behavior: smooth; scroll-behavior: smooth;
scroll-padding-top: 92px;
} }
body { body {
+3 -3
View File
@@ -14,9 +14,9 @@ const Footer: React.FC = () => {
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
> >
<h3 className="text-2xl font-bold text-green-500 mb-2">&lt;/&gt; Alex</h3> <h3 className="text-2xl font-bold text-green-500 mb-2">&lt;/&gt; Robert Bretz</h3>
<p className="text-gray-400 text-sm"> <p className="text-gray-400 text-sm">
Crafting seamless digital experiences with modern web technologies Building clean architecture, fast systems and modern web products.
</p> </p>
</motion.div> </motion.div>
@@ -80,7 +80,7 @@ const Footer: React.FC = () => {
{/* Copyright */} {/* Copyright */}
<div className="flex flex-col md:flex-row justify-between items-center"> <div className="flex flex-col md:flex-row justify-between items-center">
<p className="text-gray-500 text-sm"> <p className="text-gray-500 text-sm">
© {currentYear} Alex Johnson. All rights reserved. © {currentYear} Robert Bretz. All rights reserved.
</p> </p>
<p className="text-gray-500 text-sm mt-4 md:mt-0"> <p className="text-gray-500 text-sm mt-4 md:mt-0">
Built with 💚 using React & Tailwind CSS Built with 💚 using React & Tailwind CSS
@@ -4,7 +4,9 @@ export const useScrollToSection = () => {
const scrollToSection = useCallback((sectionId: string) => { const scrollToSection = useCallback((sectionId: string) => {
const element = document.getElementById(sectionId); const element = document.getElementById(sectionId);
if (element) { if (element) {
element.scrollIntoView({ behavior: 'smooth' }); const headerOffset = 92;
const elementPosition = element.getBoundingClientRect().top + window.pageYOffset;
window.scrollTo({ top: elementPosition - headerOffset, behavior: 'smooth' });
} }
}, []); }, []);
+2 -2
View File
@@ -2,7 +2,7 @@ export interface Skill {
name: string; name: string;
level: 'Expert' | 'Advanced' | 'Intermediate' | 'Beginner'; level: 'Expert' | 'Advanced' | 'Intermediate' | 'Beginner';
years: number; years: number;
category: 'Frontend' | 'Backend' | 'Tools' | 'Design'; category: 'Frontend' | 'Backend' | 'Tools' | 'Design' | 'Architektur' | 'Kreativ';
} }
export interface Project { export interface Project {
@@ -11,7 +11,7 @@ export interface Project {
description: string; description: string;
image: string; image: string;
tags: string[]; tags: string[];
category: 'Web Apps' | 'UI Components' | 'Full Stack'; category: 'Webanwendungen' | 'UI-Komponenten' | 'Fullstack';
link?: string; link?: string;
github?: string; github?: string;
} }