├── src ├── assets │ ├── images │ │ ├── cloud.png │ │ └── noise.gif │ └── fonts │ │ └── CosiAzure-Bold.ttf ├── components │ ├── CustomCursor.js │ ├── SectionTitle.js │ ├── Home.js │ ├── Footer.js │ ├── Bio.js │ ├── Social.js │ ├── Hero.js │ ├── Projects.js │ ├── Navbar.js │ ├── Project.js │ ├── About.js │ ├── Skills.js │ └── Contact.js ├── index.js ├── hooks │ ├── useHoverEffect.js │ ├── useSmoothScroll.js │ ├── useCustomCursor.js │ └── gsap.js ├── App.js └── index.css ├── tailwind.config.js ├── public └── index.html ├── .gitignore ├── package.json └── README.md /src/assets/images/cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayadkhan/personal-portfolio-2/HEAD/src/assets/images/cloud.png -------------------------------------------------------------------------------- /src/assets/images/noise.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayadkhan/personal-portfolio-2/HEAD/src/assets/images/noise.gif -------------------------------------------------------------------------------- /src/assets/fonts/CosiAzure-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sayadkhan/personal-portfolio-2/HEAD/src/assets/fonts/CosiAzure-Bold.ttf -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{js,jsx,ts,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /src/components/CustomCursor.js: -------------------------------------------------------------------------------- 1 | const CustomCursor = ({ innerCursorRef, outerCursorRef }) => { 2 | return ( 3 | <> 4 |
5 |
6 | 7 | ); 8 | }; 9 | 10 | export default CustomCursor; 11 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import "./index.css"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById("root")); 8 | root.render( 9 | 10 | 11 | 12 | 13 | 14 | ); 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /src/hooks/useHoverEffect.js: -------------------------------------------------------------------------------- 1 | import hoverEffect from "hover-effect"; 2 | import { useEffect } from "react"; 3 | import cloude from "../assets/images/cloud.png"; 4 | 5 | export const useHoverEffect = (el, img1, img2) => { 6 | useEffect(() => { 7 | new hoverEffect({ 8 | parent: el.current, 9 | image1: img1, 10 | image2: img2, 11 | displacementImage: cloude, 12 | intensity: 0.5, 13 | }); 14 | }, [el, img1, img2]); 15 | }; 16 | -------------------------------------------------------------------------------- /src/components/SectionTitle.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { useSectionTitleReveal } from "../hooks/gsap"; 3 | 4 | const SectionTitle = ({ title }) => { 5 | const sectionTitleRef = useRef(null); 6 | 7 | useSectionTitleReveal(sectionTitleRef); 8 | return ( 9 |
10 |

11 | {title} 12 |

13 |
14 | ); 15 | }; 16 | 17 | export default SectionTitle; 18 | -------------------------------------------------------------------------------- /src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Bio from "./Bio"; 3 | import Hero from "./Hero"; 4 | import Projects from "./Projects"; 5 | import Skills from "./Skills"; 6 | import About from "./About"; 7 | import Contact from "./Contact"; 8 | 9 | const Home = () => { 10 | return ( 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | ); 20 | }; 21 | 22 | export default Home; 23 | -------------------------------------------------------------------------------- /src/hooks/useSmoothScroll.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import Lenis from "@studio-freight/lenis"; 3 | 4 | export const useSmoothScroll = () => { 5 | useEffect(() => { 6 | const lenis = new Lenis({ 7 | duration: 1.5, 8 | easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), 9 | direction: "vertical", 10 | gestureDirection: "vertical", 11 | smooth: true, 12 | }); 13 | 14 | function raf(time) { 15 | lenis.raf(time); 16 | requestAnimationFrame(raf); 17 | } 18 | 19 | requestAnimationFrame(raf); 20 | }, []); 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { HashLink } from "react-router-hash-link/dist/react-router-hash-link.cjs.development"; 3 | import { useFooterReveal } from "../hooks/gsap"; 4 | 5 | const Footer = () => { 6 | const footerRef = useRef(null); 7 | 8 | useFooterReveal(footerRef); 9 | return ( 10 | 23 | ); 24 | }; 25 | 26 | export default Footer; 27 | -------------------------------------------------------------------------------- /src/components/Bio.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { useBioReveal } from "../hooks/gsap"; 3 | 4 | const Bio = () => { 5 | const bioRef = useRef(null); 6 | 7 | useBioReveal(bioRef, 2); 8 | 9 | return ( 10 |
11 |

12 | I am a skilled React developer with experience in building efficient and 13 | scalable web applications. I have a strong understanding of React, 14 | Redux, and related technologies, and I have a track record of delivering 15 | high-quality code on time and within budget. I am constantly learning 16 | and staying up to date with the latest developments in the React 17 | ecosystem. I am a team player who is comfortable collaborating with 18 | others and contributing to a positive work environment. 19 |

20 |
21 | ); 22 | }; 23 | 24 | export default Bio; 25 | -------------------------------------------------------------------------------- /src/hooks/useCustomCursor.js: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | export const useCustomCursor = (innerCursorRef, outerCursorRef) => { 4 | useEffect(() => { 5 | document.addEventListener("mousemove", moveCursor); 6 | 7 | function moveCursor(e) { 8 | let x = e.clientX; 9 | let y = e.clientY; 10 | 11 | // console.log(x, y); 12 | 13 | innerCursorRef.current.style.left = `${x}px`; 14 | innerCursorRef.current.style.top = `${y}px`; 15 | outerCursorRef.current.style.left = `${x}px`; 16 | outerCursorRef.current.style.top = `${y}px`; 17 | 18 | let links = Array.from(document.querySelectorAll("a")); 19 | 20 | links.forEach((link) => { 21 | link.addEventListener("mouseover", () => { 22 | innerCursorRef.current.classList.add("grow"); 23 | }); 24 | 25 | link.addEventListener("mouseleave", () => { 26 | innerCursorRef.current.classList.remove("grow"); 27 | }); 28 | }); 29 | } 30 | }, [innerCursorRef, outerCursorRef]); 31 | }; 32 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import { Route, Routes } from "react-router-dom"; 3 | import Home from "./components/Home"; 4 | import Navbar from "./components/Navbar"; 5 | import Footer from "./components/Footer"; 6 | import Social from "./components/Social"; 7 | import { useSmoothScroll } from "./hooks/useSmoothScroll"; 8 | import CustomCursor from "./components/CustomCursor"; 9 | import { useCustomCursor } from "./hooks/useCustomCursor"; 10 | 11 | const App = () => { 12 | const innerCursorRef = useRef(null); 13 | const outerCursorRef = useRef(null); 14 | 15 | useSmoothScroll(); 16 | 17 | useCustomCursor(innerCursorRef, outerCursorRef); 18 | 19 | return ( 20 |
21 |
22 | 26 | 27 | 28 | 29 | } /> 30 | 31 | 32 |
34 | ); 35 | }; 36 | 37 | export default App; 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "personal-portfolio-2", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@emailjs/browser": "^3.10.0", 7 | "@studio-freight/lenis": "^0.2.21", 8 | "@testing-library/jest-dom": "^5.16.5", 9 | "@testing-library/react": "^13.4.0", 10 | "@testing-library/user-event": "^13.5.0", 11 | "gsap": "^3.11.4", 12 | "hover-effect": "^1.1.0", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.8.0", 16 | "react-router-hash-link": "^2.4.3", 17 | "react-scripts": "5.0.1", 18 | "react-toastify": "^9.1.1", 19 | "three": "^0.149.0", 20 | "web-vitals": "^2.1.4" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | }, 46 | "devDependencies": { 47 | "tailwindcss": "^3.2.4" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/Social.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { useSocialReveal } from "../hooks/gsap"; 3 | 4 | const data = [ 5 | { 6 | id: 1, 7 | title: "Facebook", 8 | url: "https://www.facebook.com/sayad.khan.98/", 9 | }, 10 | { 11 | id: 2, 12 | title: "Twiter", 13 | url: "https://twitter.com/Abdulla58833783", 14 | }, 15 | { 16 | id: 3, 17 | title: "GitHub", 18 | url: "https://github.com/Sayadkhan", 19 | }, 20 | { 21 | id: 4, 22 | title: "Linkdin", 23 | url: "https://www.linkedin.com/in/abdullah-al-sayad-67684a20b/", 24 | }, 25 | { 26 | id: 5, 27 | title: "Leetcode", 28 | url: "", 29 | }, 30 | ]; 31 | 32 | const Social = () => { 33 | const socialRef = useRef(null); 34 | 35 | useSocialReveal(socialRef, 2); 36 | return ( 37 |
41 | {data.map((Social) => ( 42 | 49 | {Social.title} 50 | 51 | ))} 52 |
53 | ); 54 | }; 55 | 56 | export default Social; 57 | -------------------------------------------------------------------------------- /src/components/Hero.js: -------------------------------------------------------------------------------- 1 | import React, { useRef } from "react"; 2 | import { useHoverEffect } from "../hooks/useHoverEffect"; 3 | import { useImageReveal } from "../hooks/gsap"; 4 | import { useHeadlineReveal } from "../hooks/gsap"; 5 | 6 | const data = { 7 | img1: "https://res.cloudinary.com/duvzeclse/image/upload/v1675804039/Personal%20Portfolio%202/About2_tufzg3.jpg", 8 | 9 | img2: "https://res.cloudinary.com/duvzeclse/image/upload/v1675733314/Personal%20Portfolio%202/Hero2_k5iz0i.jpg", 10 | }; 11 | 12 | const Hero = () => { 13 | const heroImageRef = useRef(null); 14 | const heroHeadline1Ref = useRef(null); 15 | const heroHeadline2Ref = useRef(null); 16 | 17 | const headlines = [heroHeadline1Ref, heroHeadline2Ref]; 18 | 19 | useHoverEffect(heroImageRef, data.img1, data.img2); 20 | useImageReveal(heroImageRef, 0.5); 21 | useHeadlineReveal(headlines, 1.5); 22 | 23 | return ( 24 |
25 |
26 |
27 |

ReactJS

28 |
29 |
30 |

Developer

31 |
32 |
33 |
34 |
35 | ); 36 | }; 37 | 38 | export default Hero; 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GSAP, React, and EmailJS Portfolio Project 2 | 3 | Welcome to my GitHub portfolio project! This repository showcases my projects and skills using GSAP, React, and EmailJS. It includes a selection of notable projects, a contact form with validation and EmailJS integration, and code samples that demonstrate my programming skills. 4 | 5 | ## Table of Contents 6 | 7 | - [Demo](#demo) 8 | - [Technologies Used](#technologies-used) 9 | - [Installation and Usage](#installation-and-usage) 10 | - [Contact Form](#contact-form) 11 | - [Projects](#projects) 12 | - [Code Samples](#code-samples) 13 | - [Contributing](#contributing) 14 | - [License](#license) 15 | 16 | ## Demo 17 | 18 | Check out the live demo of this project [here](https://insert-your-demo-link-here.com). 19 | 20 | ## Technologies Used 21 | 22 | This project was built using the following technologies: 23 | 24 | - [GSAP](https://greensock.com/gsap/) 25 | - [React](https://reactjs.org/) 26 | - [EmailJS](https://www.emailjs.com/) 27 | 28 | 29 | 30 | ## Installation and Usage 31 | 32 | To run this project locally, follow these steps: 33 | 34 | 1. Clone this repository. 35 | 2. Install the dependencies by running `npm install`. 36 | 3. Start the development server by running `npm start`. 37 | 4. Open `http://localhost:3000` in your web browser. 38 | 39 | ## Contact Form 40 | 41 | This project includes a contact form that uses EmailJS to send messages directly to my inbox. The form includes form validation and a user-friendly interface built with React components. 42 | 43 | ## Projects 44 | 45 | This repository includes a selection of notable projects that demonstrate my skills and expertise using GSAP and React. Each project includes a brief description, a list of technologies used, and a link to the project repository or live demo. 46 | 47 | ## Code Samples 48 | 49 | This repository also includes code samples that demonstrate my programming skills and style, including algorithms, data structures, and design patterns. 50 | 51 | ## Contributing 52 | 53 | Feel free to contribute to this project by submitting issues or pull requests. 54 | 55 | ## License 56 | 57 | This project is licensed under the [MIT License](LICENSE). 58 | -------------------------------------------------------------------------------- /src/components/Projects.js: -------------------------------------------------------------------------------- 1 | import Project from "./Project"; 2 | import SectionTitle from "./SectionTitle"; 3 | 4 | const data = [ 5 | { 6 | id: "1", 7 | title: "React E-Gadget - An E-Commerce Website", 8 | img1: "https://res.cloudinary.com/duvzeclse/image/upload/v1675787091/Personal%20Portfolio%202/E-gadget_ew0l3k.png", 9 | img2: "https://res.cloudinary.com/duvzeclse/image/upload/v1675787084/Personal%20Portfolio%202/egadegt_2_puldka.png", 10 | description: 11 | "React E-Gadget is an e-commerce website built using React, Redux, and various other front-end technologies. The website provides customers with an intuitive and user-friendly platform for browsing, purchasing, and managing their orders.", 12 | 13 | tools: [ 14 | "React.js", 15 | "Redux.js", 16 | "Redux Toolkit", 17 | "RTK Query", 18 | "Tailwind CSS", 19 | ], 20 | liveLink: "https://visionary-granita-324a67.netlify.app/", 21 | frontEndLink: "https://github.com/Sayadkhan/e-gadget", 22 | backEndLink: "www.google.com", 23 | }, 24 | { 25 | id: "2", 26 | title: "React E-Gadget - An E-Commerce Website", 27 | img1: "https://res.cloudinary.com/duvzeclse/image/upload/v1675787182/Personal%20Portfolio%202/1_jjqs4g.jpg", 28 | img2: "https://res.cloudinary.com/duvzeclse/image/upload/v1675787180/Personal%20Portfolio%202/2_ao4k60.jpg", 29 | description: 30 | "React E-Gadget is an e-commerce website built using React, Redux, and various other front-end technologies. The website provides customers with an intuitive and user-friendly platform for browsing, purchasing, and managing their orders.", 31 | 32 | tools: [ 33 | "React.js", 34 | "Redux.js", 35 | "Redux Toolkit", 36 | "RTK Query", 37 | "Tailwind CSS", 38 | ], 39 | liveLink: "", 40 | frontEndLink: "", 41 | backEndLink: "", 42 | }, 43 | ]; 44 | 45 | const Projects = () => { 46 | return ( 47 |
48 | 49 | 50 |
51 | {data.map((project) => ( 52 | 53 | ))} 54 |
55 |
56 | ); 57 | }; 58 | 59 | export default Projects; 60 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | import { HashLink } from "react-router-hash-link/dist/react-router-hash-link.cjs.production"; 4 | 5 | import { useLinkReveal } from "../hooks/gsap"; 6 | 7 | const Navbar = ({ footerNav }) => { 8 | const link1Ref = useRef(null); 9 | const link2Ref = useRef(null); 10 | const link3Ref = useRef(null); 11 | const link4Ref = useRef(null); 12 | const link5Ref = useRef(null); 13 | const link6Ref = useRef(null); 14 | const link7Ref = useRef(null); 15 | 16 | const links = [ 17 | link1Ref, 18 | link2Ref, 19 | link3Ref, 20 | link4Ref, 21 | link5Ref, 22 | link6Ref, 23 | link7Ref, 24 | ]; 25 | 26 | useLinkReveal(links, 2); 27 | return ( 28 | 77 | ); 78 | }; 79 | 80 | export default Navbar; 81 | -------------------------------------------------------------------------------- /src/components/Project.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { useHoverEffect } from "../hooks/useHoverEffect"; 3 | import { useProjectLeftRightReveal } from "../hooks/gsap"; 4 | 5 | const Project = ({ project }) => { 6 | const projectLeftRef = useRef(null); 7 | const projectRightRef = useRef(null); 8 | 9 | const projectsRefs = [projectRightRef, projectLeftRef]; 10 | 11 | useHoverEffect(projectRightRef, project.img1, project.img2); 12 | useProjectLeftRightReveal(projectsRefs); 13 | 14 | return ( 15 |
16 |
20 | 21 | {String(project.id).padStart(2, 0)} 22 | 23 |

24 | {project.title} 25 |

26 |

{project.description}

27 | 28 | {project.tools.map((tool, i) => ( 29 | {tool} 30 | ))} 31 | 32 | 33 |
34 | 40 | Live Site 41 | 42 | 48 | Front-End code 49 | 50 | {project.backEndLink && ( 51 | 57 | Back-End code 58 | 59 | )} 60 |
61 |
62 |
66 |
67 | ); 68 | }; 69 | 70 | export default Project; 71 | -------------------------------------------------------------------------------- /src/components/About.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import { useHoverEffect } from "../hooks/useHoverEffect"; 3 | import SectionTitle from "./SectionTitle"; 4 | 5 | const data = { 6 | img1: "https://res.cloudinary.com/duvzeclse/image/upload/v1675733858/Personal%20Portfolio%202/Hero1_tqfsod.jpg", 7 | img2: "https://res.cloudinary.com/duvzeclse/image/upload/v1675804039/Personal%20Portfolio%202/About2_tufzg3.jpg", 8 | }; 9 | 10 | const About = () => { 11 | const aboutLeftRef = useRef(null); 12 | 13 | useHoverEffect(aboutLeftRef, data.img1, data.img2); 14 | 15 | return ( 16 |
17 | 18 | 19 |
20 |
21 |
22 |

23 | I am a highly skilled and motivated React JS developer with a 24 | passion for creating engaging and dynamic web applications. With 25 | extensive experience in Redux, Redux Router, and other cutting-edge 26 | technologies, I have a proven track record of delivering 27 | high-quality, scalable solutions that meet the needs of diverse 28 | businesses. Whether you are looking for a complex project or a 29 | simple, elegant design, I have the technical proficiency and 30 | creativity to bring your vision to life. 31 |

32 |

33 | In addition to my technical skills, I am a collaborative team player 34 | who is dedicated to working with others to achieve great things. I 35 | believe that communication and teamwork are the keys to success, and 36 | I am always eager to learn and grow as a developer. If you are 37 | looking for a talented React JS developer who can bring your digital 38 | vision to life, I would be honored to discuss your project with you. 39 | Let's connect and see how I can help take your business to the next 40 | level." 41 |

42 | 43 | 49 | Resume 50 | 51 |
52 |
53 |
54 | ); 55 | }; 56 | 57 | export default About; 58 | -------------------------------------------------------------------------------- /src/components/Skills.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import SectionTitle from "./SectionTitle"; 3 | import { useSkillLineReveal, useSkillTextReveal } from "../hooks/gsap"; 4 | 5 | const data = [ 6 | { id: 1, title: "HTML" }, 7 | { id: 2, title: "CSS" }, 8 | { id: 3, title: "Bootstrap" }, 9 | { id: 4, title: "Tailwind CSS" }, 10 | { id: 5, title: "JavaScript" }, 11 | { id: 6, title: "React.js" }, 12 | { id: 7, title: "React Router" }, 13 | { id: 8, title: "Redux.js" }, 14 | { id: 9, title: "Redux Toolkit" }, 15 | { id: 10, title: "Axios" }, 16 | { id: 11, title: "GSAP" }, 17 | { id: 12, title: "Firebase" }, 18 | ]; 19 | 20 | const Skills = () => { 21 | const skillItemRef = useRef([]); 22 | const skillItem2Ref = useRef([]); 23 | const skillTextRef = useRef([]); 24 | const skillText2Ref = useRef([]); 25 | 26 | useSkillLineReveal(skillItemRef.current); 27 | useSkillLineReveal(skillItem2Ref.current); 28 | useSkillTextReveal(skillTextRef.current); 29 | useSkillTextReveal(skillText2Ref.current); 30 | 31 | return ( 32 |
33 | 34 | 35 |
36 |
    37 | {data 38 | .filter((_, i) => i < Math.floor(data.length / 2)) 39 | .map((skill, i) => ( 40 |
  • (skillItemRef.current[i] = el)} 44 | > 45 |
    (skillTextRef.current[i] = el)} 48 | > 49 | 50 | {String(skill.id).padStart(2, 0).padEnd(3, ".")} 51 | 52 | {skill.title} 53 |
    54 |
  • 55 | ))} 56 |
57 |
    58 | {data 59 | .filter((_, i) => i >= Math.floor(data.length / 2)) 60 | .map((skill, i) => ( 61 |
  • (skillItem2Ref.current[i] = el)} 65 | > 66 |
    (skillText2Ref.current[i] = el)} 69 | > 70 | 71 | {String(skill.id).padStart(2, 0).padEnd(3, ".")} 72 | 73 | {skill.title} 74 |
    75 |
  • 76 | ))} 77 |
78 |
79 |
80 | ); 81 | }; 82 | 83 | export default Skills; 84 | -------------------------------------------------------------------------------- /src/components/Contact.js: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | import SectionTitle from "./SectionTitle"; 3 | import emailjs from "@emailjs/browser"; 4 | import { useContactReveal } from "../hooks/gsap"; 5 | 6 | const Contact = () => { 7 | const formRef = useRef(null); 8 | 9 | const sendEmail = (e) => { 10 | e.preventDefault(); 11 | 12 | emailjs 13 | .sendForm( 14 | process.env.REACT_APP_SERVICE_ID, 15 | process.env.REACT_APP_TEMPLATE_ID, 16 | formRef.current, 17 | process.env.REACT_APP_PUBLIC_ID 18 | ) 19 | .then( 20 | () => { 21 | console.log("message sent"); 22 | }, 23 | () => { 24 | console.log("message not sent"); 25 | } 26 | ); 27 | 28 | e.target.querySelector(".name").value = ""; 29 | e.target.querySelector(".email").value = ""; 30 | e.target.querySelector(".message").value = ""; 31 | }; 32 | 33 | const inputRef1 = useRef(null); 34 | const inputRef2 = useRef(null); 35 | const inputRef3 = useRef(null); 36 | const inputRef4 = useRef(null); 37 | 38 | const inputs = [inputRef1, inputRef2, inputRef3, inputRef4]; 39 | 40 | useContactReveal(inputs); 41 | 42 | return ( 43 |
44 | 45 | 46 |
51 |
52 | 60 |
61 |
62 | 70 |
71 |
72 |