├── 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 |
33 |
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 |
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 |
33 |
34 |
35 | {footerNav ? "Go to top" : "Abdullah Al Sayad"}
36 |
37 |
38 |
39 |
40 |
41 | Home
42 |
43 |
44 |
45 |
46 | Projects
47 |
48 |
49 |
50 |
51 | Skills
52 |
53 |
54 |
55 |
56 | About
57 |
58 |
59 |
60 |
61 | contact
62 |
63 |
64 |
65 |
72 | Resume
73 |
74 |
75 |
76 |
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 |
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 |
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 |
93 | );
94 | };
95 |
96 | export default Contact;
97 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | /* fonts */
2 | @import url("https://fonts.googleapis.com/css2?family=Michroma&display=swap");
3 |
4 | @tailwind base;
5 | @tailwind components;
6 | @tailwind utilities;
7 |
8 | @font-face {
9 | font-family: "Cosi Azure";
10 | src: url(./assets/fonts/CosiAzure-Bold.ttf);
11 | }
12 |
13 | :root {
14 | --color-dark: #050505;
15 | --color-light: #f7f7f7;
16 | }
17 |
18 | ::-webkit-scrollbar {
19 | width: 5px;
20 | }
21 |
22 | ::-webkit-scrollbar-thumb {
23 | height: 10px;
24 | background-color: rgba(247, 247, 247, 0.5);
25 | border-radius: 50px;
26 | }
27 |
28 | ::-webkit-scrollbar-thumb:hover {
29 | background-color: var(--color-light);
30 | }
31 |
32 | ::-webkit-scrollbar-track {
33 | background-color: var(--color-dark);
34 | }
35 |
36 | ::selection {
37 | background-color: #22d3ee;
38 | color: var(--color-dark);
39 | }
40 |
41 | *,
42 | *::before,
43 | *::after {
44 | cursor: none;
45 | }
46 |
47 | html,
48 | body {
49 | overflow-x: hidden;
50 | }
51 |
52 | html {
53 | font-size: 62.5%;
54 | }
55 |
56 | body {
57 | background-color: var(--color-dark);
58 | color: var(--color-light);
59 | font-family: "Michroma", sans-serif;
60 | font-size: 1.5rem;
61 | line-height: 1.6;
62 | letter-spacing: 2px;
63 | word-spacing: 3px;
64 | }
65 |
66 | .inner-cursor {
67 | position: fixed;
68 | left: 10px;
69 | z-index: 9999;
70 | width: 10px;
71 | height: 10px;
72 | border-radius: 50%;
73 | background-color: var(--color-light);
74 | transform: translate(-50%, -50%);
75 | mix-blend-mode: difference;
76 | pointer-events: none;
77 | transition: width 0.5s, height 0.5s;
78 | }
79 |
80 | .inner-cursor.grow {
81 | width: 25px;
82 | height: 25px;
83 | transition: width 0.5s, height 0.5s;
84 | }
85 |
86 | .outer-cursor {
87 | position: fixed;
88 | left: 10px;
89 | z-index: 9999;
90 | width: 25px;
91 | height: 25px;
92 | transform: translate(-50%, -50%);
93 | mix-blend-mode: difference;
94 | border: 1px solid var(--color-light);
95 | border-radius: 50%;
96 | pointer-events: none;
97 | transition: 0.1s;
98 | }
99 |
100 | input[type="submit"] {
101 | letter-spacing: inherit;
102 | word-spacing: inherit;
103 | }
104 |
105 | .app {
106 | position: relative;
107 | }
108 |
109 | .link-item {
110 | position: relative;
111 | }
112 |
113 | .link-item::after {
114 | background-color: var(--color-light);
115 | content: "";
116 | position: absolute;
117 | height: 1px;
118 | left: 50%;
119 | bottom: -2px;
120 | width: 0;
121 | transform: translateX(-50%);
122 | transition: 0.5s;
123 | }
124 |
125 | .link-item:hover::after {
126 | width: 100%;
127 | }
128 |
129 | .hero {
130 | position: relative;
131 | }
132 |
133 | .hero-img,
134 | .project-right,
135 | .about-left {
136 | height: 500px;
137 | width: 500px;
138 | overflow: hidden;
139 | }
140 |
141 | .shutter {
142 | position: absolute;
143 | z-index: 100;
144 | font-family: "Cosi Azure", sans-serif;
145 | font-size: 10rem;
146 | }
147 |
148 | .shutter-left {
149 | top: 0;
150 | left: 0;
151 | }
152 |
153 | .shutter-right {
154 | bottom: 0;
155 | right: 0;
156 | }
157 |
158 | .noise {
159 | background-image: url("./assets/images/noise.gif");
160 | height: 100vh;
161 | width: 100vw;
162 | position: fixed;
163 | top: 0;
164 | left: 0;
165 | right: 0;
166 | bottom: 0;
167 | z-index: -9999;
168 | opacity: 0.07;
169 | }
170 |
171 | .circle-left,
172 | .circle-right {
173 | height: 1000px;
174 | width: 1000px;
175 | border: 1px dashed var(--color-light);
176 | border-radius: 50%;
177 | opacity: 0.2;
178 | position: fixed;
179 | z-index: -9998;
180 | animation: spin 90s linear infinite;
181 | }
182 |
183 | @keyframes spin {
184 | to {
185 | transform: rotate(360deg);
186 | }
187 | }
188 |
189 | .circle-left {
190 | top: -40%;
191 | left: -20%;
192 | }
193 |
194 | .circle-right {
195 | bottom: -40%;
196 | right: -20%;
197 | }
198 |
199 | .section-title {
200 | font-family: "Cosi Azure", sans-serif;
201 | font-size: 10rem;
202 | line-height: 1;
203 | }
204 |
205 | .skill-item {
206 | position: relative;
207 | }
208 |
209 | .skill-item::before,
210 | .skill-item::after {
211 | content: "";
212 | height: 1px;
213 | position: absolute;
214 | bottom: 0;
215 | left: 0;
216 | background-color: var(--color-light);
217 | }
218 |
219 | .skill-item::before {
220 | width: 100%;
221 | opacity: 0.2;
222 | }
223 |
224 | .skill-item::after {
225 | width: 0;
226 | background-color: #22d3ee;
227 | transition: 0.5s;
228 | }
229 |
230 | .skill-item:hover::after {
231 | width: 100%;
232 | }
233 |
234 | .skill-number {
235 | font-size: 2rem;
236 | }
237 |
--------------------------------------------------------------------------------
/src/hooks/gsap.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import gsap from "gsap";
3 |
4 | import { ScrollTrigger } from "gsap/ScrollTrigger";
5 |
6 | gsap.registerPlugin(ScrollTrigger);
7 |
8 | export const useImageReveal = (el, delay = 0) => {
9 | useEffect(() => {
10 | gsap.fromTo(
11 | el.current,
12 | {
13 | y: "-100vh",
14 | },
15 | {
16 | y: 0,
17 | duration: 4,
18 | delay,
19 | ease: "power4.out",
20 | }
21 | );
22 | }, [el, delay]);
23 | };
24 |
25 | export const useHeadlineReveal = (items, delay = 0) => {
26 | useEffect(() => {
27 | const el = items.map((item) => item.current);
28 |
29 | gsap.fromTo(
30 | el,
31 | {
32 | y: 500,
33 | },
34 | {
35 | y: 0,
36 | duration: 1,
37 | delay,
38 | ease: "power4.out",
39 | stagger: 0.2,
40 | }
41 | );
42 | }, [items, delay]);
43 | };
44 |
45 | export const useLinkReveal = (items, delay = 0) => {
46 | useEffect(() => {
47 | const el = items.map((item) => item.current);
48 |
49 | gsap.fromTo(
50 | el,
51 | {
52 | opacity: 0,
53 | },
54 | {
55 | opacity: 1,
56 | duration: 2,
57 | delay,
58 | ease: "power4.out",
59 | stagger: 0.2,
60 | scrollTrigger: {
61 | trigger: el,
62 | },
63 | }
64 | );
65 | }, [items, delay]);
66 | };
67 |
68 | export const useBioReveal = (el, delay = 0) => {
69 | useEffect(() => {
70 | gsap.fromTo(
71 | el.current,
72 | {
73 | y: 500,
74 | },
75 | {
76 | y: 0,
77 | duration: 1.5,
78 | delay,
79 | ease: "power4.out",
80 | }
81 | );
82 | }, [el, delay]);
83 | };
84 |
85 | export const useSocialReveal = (el, delay = 0) => {
86 | useEffect(() => {
87 | gsap.fromTo(
88 | el.current,
89 | {
90 | x: -500,
91 | },
92 | {
93 | x: 0,
94 | duration: 1.5,
95 | delay,
96 | ease: "power4.out",
97 | }
98 | );
99 | }, [el, delay]);
100 | };
101 |
102 | export const useSectionTitleReveal = (el, delay = 0) => {
103 | useEffect(() => {
104 | gsap.fromTo(
105 | el.current,
106 | {
107 | y: 200,
108 | },
109 | {
110 | y: 0,
111 | duration: 1.5,
112 | delay,
113 | ease: "power4.out",
114 | scrollTrigger: {
115 | trigger: el.current,
116 | },
117 | }
118 | );
119 | }, [el, delay]);
120 | };
121 |
122 | export const useProjectLeftRightReveal = (items, delay = 0) => {
123 | useEffect(() => {
124 | const el = items.map((item) => item.current);
125 |
126 | gsap.fromTo(
127 | el,
128 | {
129 | y: 500,
130 | },
131 | {
132 | y: 0,
133 | duration: 2,
134 | delay,
135 | ease: "power4.out",
136 | stagger: 0.3,
137 | scrollTrigger: {
138 | trigger: el,
139 | },
140 | }
141 | );
142 | }, [items, delay]);
143 | };
144 |
145 | export const useSkillLineReveal = (items) => {
146 | useEffect(() => {
147 | items.forEach((el) =>
148 | gsap.to(el, {
149 | scrollTrigger: {
150 | trigger: el,
151 | onEnter() {
152 | el.classList.add("reveal");
153 | },
154 | },
155 | })
156 | );
157 | }, [items]);
158 | };
159 |
160 | export const useSkillTextReveal = (el) => {
161 | useEffect(() => {
162 | gsap.fromTo(
163 | el,
164 | {
165 | y: 200,
166 | },
167 | {
168 | y: 0,
169 | duration: 1,
170 | ease: "power4.out",
171 | stagger: 0.3,
172 | scrollTrigger: {
173 | trigger: el,
174 | },
175 | }
176 | );
177 | }, [el]);
178 | };
179 |
180 | export const useFooterReveal = (el) => {
181 | useEffect(() => {
182 | gsap.fromTo(
183 | el.current,
184 | {
185 | y: -100,
186 | },
187 | {
188 | y: 0,
189 | duration: 1,
190 | ease: "power4.out",
191 | stagger: 0.3,
192 | scrollTrigger: {
193 | trigger: el.current,
194 | },
195 | }
196 | );
197 | }, [el]);
198 | };
199 |
200 | export const useContactReveal = (items, delay = 0) => {
201 | useEffect(() => {
202 | const el = items.map((item) => item.current);
203 |
204 | gsap.fromTo(
205 | el,
206 | {
207 | y: 200,
208 | },
209 | {
210 | y: 0,
211 | duration: 2,
212 | delay,
213 | ease: "power4.out",
214 | stagger: 0.3,
215 | scrollTrigger: {
216 | trigger: el,
217 | },
218 | }
219 | );
220 | }, [items, delay]);
221 | };
222 |
--------------------------------------------------------------------------------