feat: add hero section and navigation components
Deploy Portfolio Selfmade / deploy (push) Successful in 31s
Deploy Portfolio Selfmade / deploy (push) Successful in 31s
- Created HeroSection component for the landing page. - Implemented Navigation component with smooth scrolling to sections. - Added useNavigation hook to manage active section and scroll state. - Introduced useScrollToSection hook for smooth scrolling functionality. feat: add projects section with project cards - Developed ProjectCard component to display individual project details. - Created ProjectsSection component to showcase featured projects with filtering options. - Added sample project data for demonstration. feat: add skills section with skill cards - Implemented SkillCard component to display individual skills. - Created SkillsSection component to showcase skills with category filtering. - Added sample skills data for demonstration. style: update global styles and add custom scrollbar - Imported Urbanist font and set it as the default font. - Updated body background and text colors. - Customized scrollbar styles for better aesthetics. feat: add shared components and constants - Created AnimatedSection, Button, and Footer components for reusability. - Added COLORS and GRADIENT constants for consistent theming. - Updated index files for shared components and hooks. chore: update dependencies and configuration - Added framer-motion for animations. - Updated Tailwind CSS configuration for custom animations and colors. - Adjusted TypeScript configuration for better path resolution.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
import { FaGithub } from 'react-icons/fa';
|
||||
import type { Project } from '../../../shared/types';
|
||||
|
||||
interface ProjectCardProps {
|
||||
project: Project;
|
||||
index: number;
|
||||
}
|
||||
|
||||
export const ProjectCard: React.FC<ProjectCardProps> = ({ project, index }) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
whileHover={{ y: -10 }}
|
||||
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"
|
||||
>
|
||||
{/* Image */}
|
||||
<div className="relative h-64 overflow-hidden bg-gradient-to-br from-gray-900 to-black">
|
||||
<img
|
||||
src={project.image}
|
||||
alt={project.title}
|
||||
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/80 to-transparent"></div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-6">
|
||||
{/* Category Badge */}
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-bold text-white mb-2">{project.title}</h3>
|
||||
<p className="text-gray-400 text-sm mb-4 line-clamp-2">{project.description}</p>
|
||||
|
||||
{/* Tags */}
|
||||
<div className="flex flex-wrap gap-2 mb-6">
|
||||
{project.tags.slice(0, 3).map((tag) => (
|
||||
<span
|
||||
key={tag}
|
||||
className="text-xs px-2 py-1 rounded border border-green-500/30 text-green-400"
|
||||
>
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
{project.tags.length > 3 && (
|
||||
<span className="text-xs px-2 py-1 text-gray-500">
|
||||
+{project.tags.length - 3}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Links */}
|
||||
<div className="flex gap-3">
|
||||
{project.link && (
|
||||
<motion.a
|
||||
href={project.link}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
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"
|
||||
>
|
||||
<ExternalLink size={16} />
|
||||
Live
|
||||
</motion.a>
|
||||
)}
|
||||
{project.github && (
|
||||
<motion.a
|
||||
href={project.github}
|
||||
whileHover={{ scale: 1.1 }}
|
||||
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"
|
||||
>
|
||||
<FaGithub size={16} />
|
||||
Code
|
||||
</motion.a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import React, { useState } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { projectsData } from '../data/projectsData';
|
||||
import { ProjectCard } from './ProjectCard';
|
||||
import AnimatedSection from '../../../shared/components/AnimatedSection';
|
||||
import type { Project } from '../../../shared/types';
|
||||
|
||||
type Category = 'All' | 'Web Apps' | 'UI Components' | 'Full Stack';
|
||||
|
||||
export const ProjectsSection: React.FC = () => {
|
||||
const [selectedCategory, setSelectedCategory] = useState<Category>('All');
|
||||
|
||||
const categories: Category[] = ['All', 'Web Apps', 'UI Components', 'Full Stack'];
|
||||
|
||||
const filteredProjects = projectsData.filter(
|
||||
(project: Project) =>
|
||||
selectedCategory === 'All' || project.category === selectedCategory
|
||||
);
|
||||
|
||||
return (
|
||||
<AnimatedSection id="projects" className="py-20 px-4 sm:px-6 lg:px-8">
|
||||
<div className="max-w-7xl mx-auto">
|
||||
{/* Section Header */}
|
||||
<div className="text-center mb-16">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ duration: 0.5 }}
|
||||
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>
|
||||
</motion.div>
|
||||
|
||||
<h2 className="text-4xl sm:text-5xl font-bold text-white mb-4">
|
||||
Featured Projects
|
||||
</h2>
|
||||
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
||||
Showcasing my best work and achievements
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Category Filter */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="flex flex-wrap justify-center gap-3 mb-12"
|
||||
>
|
||||
{categories.map((category) => (
|
||||
<motion.button
|
||||
key={category}
|
||||
onClick={() => setSelectedCategory(category)}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
className={`px-6 py-2 rounded-full font-medium transition-all duration-300 ${
|
||||
selectedCategory === category
|
||||
? 'bg-green-500 text-black'
|
||||
: 'border border-green-500/30 text-gray-300 hover:border-green-500/60'
|
||||
}`}
|
||||
>
|
||||
{category}
|
||||
</motion.button>
|
||||
))}
|
||||
</motion.div>
|
||||
|
||||
{/* Projects Grid */}
|
||||
<motion.div
|
||||
layout
|
||||
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
|
||||
>
|
||||
{filteredProjects.map((project: Project, index: number) => (
|
||||
<ProjectCard key={project.id} project={project} index={index} />
|
||||
))}
|
||||
</motion.div>
|
||||
</div>
|
||||
</AnimatedSection>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user