Featured Projects - Copy this React, Tailwind Component to your project
Import { useState, useEffect } from "react"; import { motion, AnimatePresence, useMotionValue, useTransform } from "framer motion"; import { FiGithub, FiExternalLink, FiSearch } from "react icons/fi"; const projects = [ { id: 1, title: "E Commerce Platform", description: "A modern e commerce platform built with React and Node.js", image: "https://images.unsplash.com/photo 1661956602116 aa6865609028", tags: ["React", "Node.js", "MongoDB"], githubLink: "https://github.com", liveLink: "https://demo.com" }, { id: 2, title: "Task Management App", description: "Collaborative task management application with real time updates", image: "https://images.unsplash.com/photo 1517694712202 14dd9538aa97", tags: ["React", "Firebase", "Tailwind"], githubLink: "https://github.com", liveLink: "https://demo.com" }, { id: 3, title: "Social Media Dashboard", description: "Analytics dashboard for social media management", image: "https://images.unsplash.com/photo 1460925895917 afdab827c52f", tags: ["Vue.js", "Python", "AWS"], githubLink: "https://github.com", liveLink: "https://demo.com" } ]; const allTags = [...new Set(projects.flatMap(project => project.tags))]; const ProjectCard = ({ project }) => { const x = useMotionValue(0); const y = useMotionValue(0); const rotateX = useTransform(y, [ 100, 100], [30, 30]); const rotateY = useTransform(x, [ 100, 100], [ 30, 30]); const handleMouseMove = (event) => { const rect = event.currentTarget.getBoundingClientRect(); const width = rect.width; const height = rect.height; const mouseX = event.clientX rect.left; const mouseY = event.clientY rect.top; const xPct = mouseX / width 0.5; const yPct = mouseY / height 0.5; x.set(xPct * 100); y.set(yPct * 100); }; const handleMouseLeave = () => { x.set(0); y.set(0); }; return ( <motion.div style={{ rotateX, rotateY, transformStyle: "preserve 3d" }} onMouseMove={handleMouseMove} onMouseLeave={handleMouseLeave} className="bg [#151030] rounded lg overflow hidden shadow lg hover:shadow xl transition shadow duration 300 transform gpu" > <div className="relative group"> <img src={project.image} alt={project.title} className="w full h 48 object cover transition transform duration 300 group hover:scale 105" /> <div className="absolute inset 0 bg black bg opacity 50 opacity 0 group hover:opacity 100 transition opacity duration 300 flex items center justify center space x 4"> <a href={project.githubLink} target="_blank" rel="noopener noreferrer" className="p 2 bg white rounded full hover:bg gray 100 transition colors duration 200" aria label="View GitHub Repository" > <FiGithub className="w 6 h 6" /> </a> <a href={project.liveLink} target="_blank" rel="noopener noreferrer" className="p 2 bg white rounded full hover:bg gray 100 transition colors duration 200" aria label="View Live Demo" > <FiExternalLink className="w 6 h 6" /> </a> </div> </div> <div className="p 6"> <h3 className="text xl font bold text white mb 2">{project.title}</h3> <p className="text gray 300 text lg mb 4">{project.description}</p> <div className="flex flex wrap gap 2"> {project.tags.map(tag => ( <span key={tag} className="px 3 py 1 bg blue 900 text blue 200 rounded full text sm" > {tag} </span> ))} </div> </div> </motion.div> ); }; const ProjectCards = () => { const [selectedTags, setSelectedTags] = useState([]); const [searchQuery, setSearchQuery] = useState(""); const [filteredProjects, setFilteredProjects] = useState(projects); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const filterProjects = () => { setIsLoading(true); const filtered = projects.filter(project => { const matchesTags = selectedTags.length === 0 || selectedTags.every(tag => project.tags.includes(tag)); const matchesSearch = project.title.toLowerCase().includes(searchQuery.toLowerCase()) || project.description.toLowerCase().includes(searchQuery.toLowerCase()); return matchesTags && matchesSearch; }); setTimeout(() => { setFilteredProjects(filtered); setIsLoading(false); }, 500); }; filterProjects(); }, [selectedTags, searchQuery]); const toggleTag = (tag) => { setSelectedTags(prev => prev.includes(tag) ? prev.filter(t => t !== tag) : [...prev, tag] ); }; return ( <div className="max w 7xl mx auto px 4 py 12 "> <div className="mb 8"> <h2 className="text 4xl font bold text white mb 6">My Projects</h2> <div className="relative mb 6"> <input type="text" placeholder=" Search projects..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="w full px 4 text lg py 2 rounded lg border border gray 600 bg [#151030] text white focus:ring 2 focus:ring blue 500 focus:border blue 500 pl 10" /> <FiSearch className="absolute left 4 top 3 text gray 400 size 6 " /> </div> <div className="flex flex wrap gap 2"> {allTags.map(tag => ( <button key={tag} onClick={() => toggleTag(tag)} className={`px 4 py 2 rounded full text sm font medium transition all duration 200 ${ selectedTags.includes(tag) ? "bg blue 600 text white" : "bg [#251045] text gray 300 hover:bg [#351055]" }`} > {tag} </button> ))} </div> </div> {isLoading ? ( <div className="grid grid cols 1 md:grid cols 2 lg:grid cols 3 gap 6"> {[1, 2, 3].map(n => ( <div key={n} className="animate pulse"> <div className="bg [#251045] h 48 rounded t lg"></div> <div className="p 4 bg [#151030] rounded b lg shadow"> <div className="h 6 bg [#251045] rounded mb 4"></div> <div className="h 4 bg [#251045] rounded w 3/4 mb 4"></div> <div className="flex gap 2"> <div className="h 6 w 16 bg [#251045] rounded full"></div> <div className="h 6 w 16 bg [#251045] rounded full"></div> </div> </div> </div> ))} </div> ) : ( <AnimatePresence> <div className="grid grid cols 1 md:grid cols 2 lg:grid cols 3 gap 6"> {filteredProjects.map(project => ( <ProjectCard key={project.id} project={project} /> ))} </div> </AnimatePresence> )} {!isLoading && filteredProjects.length === 0 && ( <div className="text center py 12"> <p className="text gray 300 text lg"> No projects found matching your criteria. </p> </div> )} </div> ); }; export default ProjectCards; modify this code and add featured project section here add 3 4 projects and keep it seperate from others
