├── public
├── _redirects
└── index.html
├── src
├── components
│ ├── Home.js
│ ├── Footer.js
│ ├── About.js
│ ├── BlogItem.js
│ ├── GalleryItem.js
│ ├── Navbar.js
│ ├── Gallery.js
│ ├── NotFound.js
│ ├── Featured.js
│ ├── Hero.js
│ └── Blog.js
├── index.js
├── hooks
│ ├── useSmoothScroll.js
│ └── gsap.js
├── App.js
└── index.css
├── .gitignore
├── package.json
└── README.md
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Immemorial
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import About from "./About";
2 | import Featured from "./Featured";
3 | import Gallery from "./Gallery";
4 | import Hero from "./Hero";
5 |
6 | const Home = () => {
7 | return (
8 |
14 | );
15 | };
16 |
17 | export default Home;
18 |
--------------------------------------------------------------------------------
/.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 |
12 | # misc
13 | .DS_Store
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 |
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { BrowserRouter } from "react-router-dom";
4 | import App from "./App";
5 | import "./index.css";
6 |
7 | const root = ReactDOM.createRoot(document.getElementById("root"));
8 | root.render(
9 |
10 |
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/src/hooks/useSmoothScroll.js:
--------------------------------------------------------------------------------
1 | import Lenis from "@studio-freight/lenis";
2 | import { useEffect } from "react";
3 |
4 | export const useSmoothScroll = () => {
5 | const lenis = new Lenis({
6 | duration: 1.5,
7 | easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
8 | direction: "vertical",
9 | gestureDirection: "vertical",
10 | smooth: true,
11 | });
12 |
13 | // creating frame inside useEffect()
14 | useEffect(() => {
15 | function raf(time) {
16 | lenis.raf(time);
17 | requestAnimationFrame(raf);
18 | }
19 |
20 | requestAnimationFrame(raf);
21 | }, []);
22 | };
23 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { useGsapFooterHeadline } from "../hooks/gsap";
3 |
4 | const Footer = () => {
5 | // Creating reference
6 | const footerRef = useRef(null);
7 | const footerHeadlineRef = useRef(null);
8 |
9 | // Calling custom hook
10 | useGsapFooterHeadline(footerHeadlineRef, footerRef);
11 |
12 | return (
13 |
14 | Bonjour
15 |
16 | © {new Date().getFullYear()} Immemorial. Crafted by yours truly
17 |
18 |
19 | );
20 | };
21 |
22 | export default Footer;
23 |
--------------------------------------------------------------------------------
/src/components/About.js:
--------------------------------------------------------------------------------
1 | const About = () => {
2 | return (
3 |
4 | About
5 |
6 | Explore the lost treasures and shining stars of the 1990s! Find your
7 | favorite cartoons, TV shows, music albums, & more with easy filtering
8 | functionality. With Immemorial, stay up-to-date with all your 90s
9 | favorites while turning back time.
10 |
11 |
12 | What's the only era that never seems to end? The 90s! Journey through
13 | appreciating items from 90s TV, music, and art. See if you remember old
14 | toys, cartoons, or prints of such. Indulge in some nostalgia before our
15 | world falls back into the dark ages.
16 |
17 |
18 | );
19 | };
20 |
21 | export default About;
22 |
--------------------------------------------------------------------------------
/src/components/BlogItem.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { useGsapBlogPhotoReveal } from "../hooks/gsap";
3 |
4 | const BlogItem = ({ image }) => {
5 | // Creating reference
6 | const blogItemRef = useRef(null);
7 | const blogTitleRef = useRef(null);
8 | const blogImgRef = useRef(null);
9 |
10 | // Calling custom hooks
11 |
12 | // useGsapBlogPhotoReveal(blogImgRef);
13 |
14 | return (
15 |
16 |
17 |
18 |
19 |
20 |
21 | {image.title}
22 |
23 | {/*
{image.description}
*/}
24 |
25 |
26 | );
27 | };
28 |
29 | export default BlogItem;
30 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "project-immemorial",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@studio-freight/lenis": "^0.2.26",
7 | "@testing-library/jest-dom": "^5.16.5",
8 | "@testing-library/react": "^13.4.0",
9 | "@testing-library/user-event": "^13.5.0",
10 | "gsap": "^3.11.3",
11 | "react": "^18.2.0",
12 | "react-dom": "^18.2.0",
13 | "react-router-dom": "^6.4.4",
14 | "react-scripts": "5.0.1",
15 | "web-vitals": "^2.1.4"
16 | },
17 | "scripts": {
18 | "start": "react-scripts start",
19 | "build": "react-scripts build",
20 | "test": "react-scripts test",
21 | "eject": "react-scripts eject"
22 | },
23 | "eslintConfig": {
24 | "extends": [
25 | "react-app",
26 | "react-app/jest"
27 | ]
28 | },
29 | "browserslist": {
30 | "production": [
31 | ">0.2%",
32 | "not dead",
33 | "not op_mini all"
34 | ],
35 | "development": [
36 | "last 1 chrome version",
37 | "last 1 firefox version",
38 | "last 1 safari version"
39 | ]
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/GalleryItem.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | useGsapGalleryTitle,
4 | useGsapGalleryCategory,
5 | useGsapGalleryImage,
6 | } from "../hooks/gsap";
7 |
8 | const GalleryItem = ({ image }) => {
9 | // Creating Reference
10 | const galleryTitleRef = useRef(null);
11 | const galleryCategoryRef = useRef(null);
12 | const galleryImageRef = useRef(null);
13 |
14 | // Calling custom hooks
15 | useGsapGalleryTitle(galleryTitleRef, galleryImageRef);
16 | useGsapGalleryCategory(galleryCategoryRef, galleryImageRef);
17 | useGsapGalleryImage(galleryImageRef);
18 |
19 | return (
20 |
21 |
22 | {image.title}
23 |
24 |
25 | {image.category}
26 |
27 |
32 |
33 | );
34 | };
35 |
36 | export default GalleryItem;
37 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Routes, Route } from "react-router-dom";
2 | import About from "./components/About";
3 | import Blog from "./components/Blog";
4 | import Featured from "./components/Featured";
5 | import Footer from "./components/Footer";
6 | import Gallery from "./components/Gallery";
7 | import Home from "./components/Home";
8 | import Navbar from "./components/Navbar";
9 | import NotFound from "./components/NotFound";
10 | import { useSmoothScroll } from "./hooks/useSmoothScroll";
11 |
12 | const App = () => {
13 | // Calling custom hook for smoothing scroll behaivior
14 | useSmoothScroll();
15 | return (
16 |
17 |
18 |
19 | } />
20 | } />
21 | } />
22 | } />
23 | } />
24 | } />
25 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default App;
32 |
--------------------------------------------------------------------------------
/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { Link } from "react-router-dom";
3 | import { useGsapDownStagger } from "../hooks/gsap";
4 |
5 | const Navbar = () => {
6 | // Creating reference of every element
7 | const li1 = useRef(null);
8 | const li2 = useRef(null);
9 | const li3 = useRef(null);
10 | const logoRef = useRef(null);
11 | const blogRef = useRef(null);
12 |
13 | const liArr = [li1, li2, li3];
14 | const favArr = [blogRef];
15 | const logoArr = [logoRef];
16 |
17 | // Calling custom hooks
18 | useGsapDownStagger(liArr, 1.5);
19 | useGsapDownStagger(logoArr, 2.2);
20 | useGsapDownStagger(favArr, 2.6);
21 |
22 | return (
23 |
24 |
25 |
26 | Featured
27 |
28 |
29 | About
30 |
31 |
32 | Gallery
33 |
34 |
35 |
36 |
37 |
Immemorial
38 |
39 |
40 |
41 | Blog
42 |
43 |
44 | );
45 | };
46 |
47 | export default Navbar;
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Immemorial - your guide to the golden era of the 90s!
2 |
3 | Immerse yourself in the nostalgia of the 90s with our web application, built using React, React Router, and GSAP animations.
4 |
5 | ## Description
6 |
7 | Our web application, called "Immemorial", is a platform designed for 90s kids to explore and reminisce about their favorite TV shows, music albums, and cartoons. With a user-friendly interface and easy-to-use filtering functionality, Immemorial makes it easy to discover your favorite 90s memories. The application is built using the following technologies:
8 |
9 | - React: a popular JavaScript library for building user interfaces.
10 | - React Router: a library that provides routing functionality to your React applications.
11 | - GSAP: an animation library that allows for the creation of smooth and engaging animations on the web.
12 |
13 | ## Installation
14 |
15 | To install and run this application on your local machine, follow these steps:
16 |
17 | 1. Clone this repository to your local machine.
18 | 2. In your terminal, navigate to the project directory and run `npm install` to install the necessary dependencies.
19 | 3. Once the installation is complete, run `npm start` to start the application.
20 | 4. Open your web browser and navigate to `http://localhost:3000` to view the application.
21 |
22 | ## Visit the live site
23 |
24 | [Immemorial](https://project-immemorial.netlify.app/)
25 |
--------------------------------------------------------------------------------
/src/components/Gallery.js:
--------------------------------------------------------------------------------
1 | import GalleryItem from "./GalleryItem";
2 |
3 | const images = [
4 | {
5 | id: 1,
6 | src: "https://images.pexels.com/photos/4842487/pexels-photo-4842487.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
7 | title: "Arcade playtime for 90's kids",
8 | category: "Arcade Games",
9 | },
10 | {
11 | id: 2,
12 | src: "https://images.pexels.com/photos/3356608/pexels-photo-3356608.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
13 | title: "No signal - no transmission",
14 | category: "TV",
15 | },
16 | {
17 | id: 3,
18 | src: "https://images.pexels.com/photos/12668238/pexels-photo-12668238.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
19 | title: "Retro Closures",
20 | category: "Boombox",
21 | },
22 | {
23 | id: 4,
24 | src: "https://images.pexels.com/photos/12204293/pexels-photo-12204293.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
25 | title: "Vinyl Loveless Happiness",
26 | category: "Vinyl Record",
27 | },
28 | ];
29 |
30 | const Gallery = () => {
31 | return (
32 |
33 | Gallery
34 |
35 | {images.map((image) => (
36 |
37 | ))}
38 |
39 |
40 | );
41 | };
42 |
43 | export default Gallery;
44 |
--------------------------------------------------------------------------------
/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { useGsapNotFoundHeadline, useGsapNotFoundImg } from "../hooks/gsap";
3 |
4 | const NotFound = ({ needFullHeight }) => {
5 | // Creating reference
6 | const leftHeadlineRef = useRef(null);
7 | const rightHeadlineRef = useRef(null);
8 | const leftImgRef = useRef(null);
9 | const rightImgRef = useRef(null);
10 |
11 | // Calling custom hooks
12 | useGsapNotFoundHeadline(leftHeadlineRef);
13 | useGsapNotFoundHeadline(rightHeadlineRef, "100vw");
14 | useGsapNotFoundImg(rightImgRef);
15 | useGsapNotFoundImg(leftImgRef);
16 |
17 | return (
18 |
19 |
20 | Sorry, we couldn't
21 |
22 |
23 |
24 |
28 |
29 |
30 |
31 |
35 |
36 |
37 |
38 | Find that page
39 |
40 |
41 | );
42 | };
43 |
44 | export default NotFound;
45 |
--------------------------------------------------------------------------------
/src/components/Featured.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | useGsapFeatureLeftShutterUnveil,
4 | useGsapFeatureRightShutterUnveil,
5 | } from "../hooks/gsap";
6 |
7 | const Featured = () => {
8 | // Creating Reference
9 | const featureRef = useRef(null);
10 | const featureLeftShutterRef = useRef(null);
11 | const featureRightShutterRef = useRef(null);
12 |
13 | // Calling custom hooks for animating feature images
14 | useGsapFeatureLeftShutterUnveil(featureLeftShutterRef, featureRef);
15 | useGsapFeatureRightShutterUnveil(featureRightShutterRef, featureRef);
16 |
17 | return (
18 |
19 | Featured
20 |
21 |
22 |
90'S TELEPHONE
23 |
27 |
31 |
32 |
33 |
90'S CASSETTE PLAYER
34 |
38 |
42 |
43 |
44 |
45 | );
46 | };
47 |
48 | export default Featured;
49 |
--------------------------------------------------------------------------------
/src/components/Hero.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | useGsapShutterUnveil,
4 | useGsapPhotoDropping,
5 | useGsapPhotoLevitate,
6 | } from "../hooks/gsap";
7 |
8 | const Hero = () => {
9 | // Creating Reference for hero texts
10 | const heroRef = useRef(null);
11 | const shutter1 = useRef(null);
12 | const shutter2 = useRef(null);
13 |
14 | // Creating Reference for hero images
15 | const photo1Ref = useRef(null);
16 | const photo2Ref = useRef(null);
17 | const photo3Ref = useRef(null);
18 | const photo4Ref = useRef(null);
19 | const photo5Ref = useRef(null);
20 |
21 | const photosArr = [photo1Ref, photo2Ref, photo3Ref, photo4Ref, photo5Ref];
22 |
23 | // Calling custom hooks for animating hero texts
24 | useGsapShutterUnveil(shutter1, 0, heroRef);
25 | useGsapShutterUnveil(shutter2, 0.2, heroRef);
26 |
27 | // Calling custom hooks for animating hero images
28 | useGsapPhotoDropping(photosArr);
29 | useGsapPhotoLevitate(photosArr, heroRef);
30 |
31 | return (
32 |
33 |
34 | Ethereal
35 |
36 |
37 | Canvas
38 |
39 |
40 |
41 |
49 |
57 |
65 |
73 |
81 |
82 |
83 | );
84 | };
85 |
86 | export default Hero;
87 |
--------------------------------------------------------------------------------
/src/components/Blog.js:
--------------------------------------------------------------------------------
1 | import BlogItem from "./BlogItem";
2 |
3 | const images = [
4 | {
5 | id: 1,
6 | src: "https://images.pexels.com/photos/11691807/pexels-photo-11691807.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
7 | title: "Tom & Jerry",
8 | description:
9 | "Tom and Jerry was one show we never missed after coming from school. Despite the love-hate relationship between the cat and the mouse, this show taught us the importance of friendship.",
10 | },
11 | {
12 | id: 2,
13 | src: "https://images.unsplash.com/photo-1607748851687-ba9a10438621?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=987&q=80",
14 | title: "Friends",
15 | description:
16 | "Friends is a Comedy TV show about 6 friends who go through just about every life experience imaginable together.I was really hooked on it. I remember it was on at 8:00 every Thursday and it was so important to be home on time for it.I also remember that my friends and I used to use catchphrases from the show. Like, “Could I BE any more ______?” or “How you doin’?” or “OH. MY. GOD!!!!”",
17 | },
18 | {
19 | id: 3,
20 | src: "https://images.pexels.com/photos/4575406/pexels-photo-4575406.jpeg",
21 | title: "Virgin Cola",
22 | description:
23 | "Fair play to Richard Branson, he gets most things right. The madcap entrepreneur owns the world famous Virgin brand and runs successful airlines, train networks, music festivals and even now his own Formula One racing team. But Virgin Cola was a mistake.",
24 | },
25 | {
26 | id: 4,
27 | src: "https://images.pexels.com/photos/9135083/pexels-photo-9135083.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
28 | title: "Yo-yos",
29 | description:
30 | "They say everything comes back around and in the late 90s the yoyo did return to the playgrounds of Britain, but unlike when my mum and dad’s generation were fascinated by them going up and down a piece of string, we took it to the next level.",
31 | },
32 | {
33 | id: 5,
34 | src: "https://images.pexels.com/photos/12629104/pexels-photo-12629104.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
35 | title: "Spice Girls",
36 | description:
37 | "Simply the greatest thing to come out of the 90s, the Spice Girls dominated the entertainment industry during the second half of the decade and became the biggest thing on the planet overnight.",
38 | },
39 |
40 | {
41 | id: 6,
42 | src: "https://images.pexels.com/photos/275033/pexels-photo-275033.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
43 | title: "Sega Mega Drive",
44 | description:
45 | "The Sega Mega Drive is to games consoles in the 90s as George Best was to football 30 year previously. Video gaming and football were obviously around before them but once they appeared on the scene the future would change forever.",
46 | },
47 |
48 | {
49 | id: 7,
50 | src: "https://images.pexels.com/photos/11795116/pexels-photo-11795116.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load",
51 | title: "Swing",
52 | description:
53 | "Most of the girls are in love with swings irrespective of their age. Swinging in a swing makes them happy and gives them a peaceful feeling. Swinging just makes each of us calm and happy.",
54 | },
55 | {
56 | id: 8,
57 | src: "https://images.pexels.com/photos/9067719/pexels-photo-9067719.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
58 | title: "Cricket",
59 | description:
60 | "The only childhood play, every bengali boy inherits as a child is Cricket.After Asr prayer, every boy would leave the house to play cricket. First they all divided into two teams, then tossed which team would bat first. Then they started playing.Sometimes there would be prizes for the winning team which added to the fun of the game.",
61 | },
62 | {
63 | id: 9,
64 | src: "https://images.unsplash.com/photo-1600077063877-22118d6290eb?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1170&q=80",
65 | title: "Football",
66 | description:
67 | "All the boys loved to play football during monsoons. They had more fun playing football in the rain. After playing football in the rain, they all bathed together in the pond and returned home.",
68 | },
69 | {
70 | id: 10,
71 | src: "https://images.pexels.com/photos/10347115/pexels-photo-10347115.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
72 | title: "Kabaddi",
73 | description:
74 | "Kabaddi is a form of bengali wrestling.The sport not only rekindles childhood memories, but also it is easy to observe people narrate their kabaddi stories every time there is a discussion on Pro Kabaddi or World Kabaddi League. Be it of getting ones t-shirt torn or beating up the high school bully. There is always a story of a lesser popular David and Goliath in that.",
75 | },
76 |
77 | {
78 | id: 11,
79 | src: "https://www.thesun.co.uk/wp-content/uploads/2022/09/LM_slapbracelet_offplat.jpg?strip=all&quality=100&w=1920&h=1080&crop=1",
80 | title: "Slap Bracelet",
81 | description:
82 | "The slap bracelet had two faces. It wasn't just a toy that could be extended and reformed, it was a serious fashion statement — perfect for kids on the cusp of childhood and adulthood, standing between the pre- and post-Internet eras. '90s kids liked their toys fashionable and their fashion playful, and slap bracelets gave them exactly that.",
83 | },
84 | // {
85 | // id: 12,
86 | // src: "https://images.pexels.com/photos/3109671/pexels-photo-3109671.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
87 | // title: "Load-shedding",
88 | // description:
89 | // "At one time in Bangladesh there were many problems of load shedding. Every evening the electricity went out. When the electricity went out, all the children would go out of the house. They used to talk together, have fun, play together. But now these things are not seen anymore.",
90 | // },
91 | ];
92 |
93 | const Blog = ({ needFullHeight }) => {
94 | return (
95 |
98 | Blog
99 |
100 |
101 | {images.map((image) => (
102 |
103 | ))}
104 |
105 |
106 | );
107 | };
108 |
109 | export default Blog;
110 |
--------------------------------------------------------------------------------
/src/hooks/gsap.js:
--------------------------------------------------------------------------------
1 | // Creating custom hook for animating hero texts
2 | import { useEffect } from "react";
3 | import gsap, { Expo } from "gsap";
4 | import { ScrollTrigger } from "gsap/ScrollTrigger";
5 |
6 | gsap.registerPlugin(ScrollTrigger); // registering plugin
7 |
8 | export const useGsapShutterUnveil = (item, delay = 0, trig) => {
9 | useEffect(() => {
10 | const element = item.current;
11 |
12 | gsap.fromTo(
13 | element,
14 | {
15 | height: "100%",
16 | },
17 | {
18 | height: "0",
19 | duration: 2,
20 | ease: Expo.easeInOut,
21 | delay: delay,
22 | scrollTrigger: {
23 | trigger: trig.current,
24 | toggleActions: "play reverse play reverse",
25 | },
26 | }
27 | );
28 | }, []);
29 | };
30 |
31 | // Creating custom hook for animating navbar content
32 | export const useGsapDownStagger = (links, delay = 0) => {
33 | useEffect(() => {
34 | // creating element
35 | const el = links.map((link) => link.current);
36 |
37 | // creating twin
38 | gsap.fromTo(
39 | el,
40 | {
41 | y: "-100%",
42 | opacity: 0,
43 | },
44 | {
45 | y: 0,
46 | opacity: 1,
47 | duration: 1.2, // restricted duration
48 | stagger: 0.1, // stagger delay:comes one after another
49 | ease: Expo.easeInOut, // timing function
50 | delay: delay,
51 | }
52 | );
53 | }, []);
54 | };
55 |
56 | // Creating custom hook for dropping hero images
57 | export const useGsapPhotoDropping = (photos) => {
58 | useEffect(() => {
59 | const el = photos.map((photo) => photo.current);
60 |
61 | gsap.fromTo(
62 | el,
63 | {
64 | y: "-100vh",
65 | scale: 0, // used in order to scale down photos
66 | },
67 | {
68 | y: 0,
69 | scale: 1, // used in order to scale up photos
70 | duration: 2,
71 | stagger: 0.2,
72 | delay: 2.2,
73 | ease: Expo.easeInOut,
74 | }
75 | );
76 | }, []);
77 | };
78 |
79 | // Creating custom hook for levitating hero images(parallax effect)
80 | export const useGsapPhotoLevitate = (photos, trig) => {
81 | useEffect(() => {
82 | const el = photos.map((photo) => photo.current);
83 |
84 | gsap.fromTo(
85 | el,
86 | {
87 | y: 0,
88 | },
89 | {
90 | y: "-30%",
91 |
92 | ease: Expo.easeInOut,
93 | scrollTrigger: {
94 | trigger: trig.current,
95 | scrub: 1,
96 | toggleActions: "play reverse play reverse",
97 | },
98 | }
99 | );
100 | }, []);
101 | };
102 |
103 | // Creating custom hook for animating features left image
104 |
105 | export const useGsapFeatureLeftShutterUnveil = (item, trig) => {
106 | useEffect(() => {
107 | const el = item.current;
108 |
109 | gsap.fromTo(
110 | el,
111 | {
112 | height: "100%",
113 | },
114 | {
115 | height: 0,
116 | duration: 1.2,
117 | ease: Expo.easeInOut,
118 | scrollTrigger: {
119 | trigger: trig.current,
120 | start: "top center",
121 | end: "bottom center",
122 | toggleActions: "play reverse play reverse",
123 | },
124 | }
125 | );
126 | }, []);
127 | };
128 |
129 | // Creating custom hook for animating features right image
130 | export const useGsapFeatureRightShutterUnveil = (item, trig) => {
131 | useEffect(() => {
132 | const el = item.current;
133 |
134 | gsap.fromTo(
135 | el,
136 | {
137 | width: "100%",
138 | },
139 | {
140 | width: 0,
141 | duration: 1.2,
142 | ease: Expo.easeInOut,
143 | scrollTrigger: {
144 | trigger: trig.current,
145 | start: "top center",
146 | end: "bottom center",
147 | toggleActions: "play reverse play reverse",
148 | },
149 | }
150 | );
151 | }, []);
152 | };
153 |
154 | // Creating custom hook for animating gallery image
155 | export const useGsapGalleryImage = (image) => {
156 | useEffect(() => {
157 | const el = image.current;
158 |
159 | gsap.fromTo(
160 | el,
161 | {
162 | x: 0,
163 | width: 0,
164 | },
165 | {
166 | x: "30%",
167 | width: "100%",
168 | duration: 1,
169 | ease: Expo.easeInOut,
170 | scrollTrigger: {
171 | trigger: el,
172 | start: "top center",
173 | end: "bottom top",
174 | toggleActions: "play reverse play reverse",
175 | },
176 | }
177 | );
178 | }, []);
179 | };
180 |
181 | // Creating custom hook for animating gallery title
182 | export const useGsapGalleryTitle = (item, trig) => {
183 | useEffect(() => {
184 | const el = item.current;
185 |
186 | gsap.fromTo(
187 | el,
188 | {
189 | x: "30%",
190 | },
191 | {
192 | x: 0,
193 |
194 | duration: 1,
195 | ease: Expo.easeInOut,
196 | scrollTrigger: {
197 | trigger: trig.current,
198 | start: "top center",
199 | end: "bottom top",
200 | toggleActions: "play reverse play reverse",
201 | },
202 | }
203 | );
204 | }, []);
205 | };
206 |
207 | // Creating custom hook for animating gallery category
208 | export const useGsapGalleryCategory = (item, trig) => {
209 | useEffect(() => {
210 | const el = item.current;
211 |
212 | gsap.fromTo(
213 | el,
214 | {
215 | x: "-100vw",
216 | },
217 | {
218 | x: 0,
219 | duration: 1,
220 | ease: Expo.easeInOut,
221 | scrollTrigger: {
222 | trigger: trig.current,
223 | start: "top center",
224 | end: "bottom top",
225 | toggleActions: "play reverse play reverse",
226 | },
227 | }
228 | );
229 | }, []);
230 | };
231 |
232 | // Creating custom hook for animating footer headline
233 | export const useGsapFooterHeadline = (item, trig) => {
234 | useEffect(() => {
235 | const el = item.current;
236 |
237 | gsap.fromTo(
238 | el,
239 | {
240 | y: "-100%",
241 | },
242 | {
243 | y: 0,
244 | duration: 1,
245 | ease: Expo.easeInOut,
246 | scrollTrigger: {
247 | trigger: trig.current,
248 | toggleActions: "play",
249 | },
250 | }
251 | );
252 | }, []);
253 | };
254 |
255 | // Creating custom hook for animating the headline of not found page
256 |
257 | export const useGsapNotFoundHeadline = (item, vw = "-100vw") => {
258 | useEffect(() => {
259 | const el = item.current;
260 |
261 | gsap.fromTo(
262 | el,
263 | {
264 | x: vw,
265 | },
266 | {
267 | x: 0,
268 | duration: 1.5,
269 | ease: Expo.easeInOut,
270 | }
271 | );
272 | }, []);
273 | };
274 |
275 | // Creating custom hook for animating the headline of not found page
276 |
277 | export const useGsapNotFoundImg = (img) => {
278 | useEffect(() => {
279 | const el = img.current;
280 | gsap.fromTo(
281 | el,
282 | {
283 | scale: 0,
284 | borderRadius: "50%",
285 | },
286 | {
287 | scale: 1,
288 | borderRadius: 0,
289 | duration: 4,
290 | delay: 1,
291 | ease: "elastic",
292 | }
293 | );
294 | }, []);
295 | };
296 |
297 | // custom hook
298 |
299 | export const useGsapBlogPhotoReveal = (img) => {
300 | useEffect(() => {
301 | const el = img.current;
302 |
303 | gsap.fromTo(
304 | el,
305 | {
306 | width: 0,
307 | },
308 | {
309 | width: "100%",
310 | duration: 2,
311 | ease: Expo.easeInOut,
312 | scrollTrigger: {
313 | trigger: img.current,
314 | toggleActions: "play reverse play reverse",
315 | },
316 | }
317 | );
318 | }, []);
319 | };
320 |
321 | // export const useGsapBlogTitleReveal = (item, trig);
322 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | /* 1. Poppins */
2 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap");
3 |
4 | /* 2. Bai Jamjuree */
5 | @import url("https://fonts.googleapis.com/css2?family=Bai+Jamjuree:wght@200;300;400;500;600;700&display=swap");
6 |
7 | /* 3. Syncopate */
8 | @import url("https://fonts.googleapis.com/css2?family=Syncopate:wght@400;700&display=swap");
9 |
10 | /* 4. Bodoni Moda */
11 | @import url("https://fonts.googleapis.com/css2?family=Bodoni+Moda:wght@400;500;600;700;800;900&display=swap");
12 |
13 | /* **********CUSTOM VARIABLE DECLARATION********** */
14 | :root {
15 | --color-primary: #d53f41;
16 | --color-dark: #626262;
17 | --color-darker: #464646;
18 | --color-light: #dbd8d6;
19 | --color-lighter: #f5f0ec;
20 | }
21 |
22 | /* **************UNIVERSAL SELECTION************** */
23 |
24 | * {
25 | margin: 0;
26 | padding: 0;
27 | list-style: none;
28 | text-decoration: none;
29 | box-sizing: border-box;
30 | }
31 |
32 | /* ****************GENERAL STYLES**************** */
33 |
34 | html,
35 | body {
36 | overflow-x: hidden;
37 | }
38 |
39 | html {
40 | font-size: 62.5%;
41 | }
42 |
43 | body {
44 | font-family: "Poppins", sans-serif;
45 | font-size: 2rem;
46 | font-weight: 400;
47 | line-height: 1.7;
48 | letter-spacing: 1px;
49 | background-color: var(--color-lighter);
50 | color: var(--color-darker);
51 | }
52 |
53 | .wrapper {
54 | margin: 0 5vw;
55 | }
56 |
57 | section {
58 | padding: 10vw 0;
59 | }
60 |
61 | .section-title {
62 | font-family: "Syncopate", sans-serif;
63 | font-size: 1.5rem;
64 | padding-bottom: 5vw;
65 | text-transform: lowercase;
66 | color: var(--color-primary);
67 | }
68 |
69 | /* HELPER CLASS */
70 |
71 | .min-h-100vh {
72 | min-height: 100vh;
73 | }
74 |
75 | /* ******************NAVBAR STYLES****************** */
76 |
77 | .navbar {
78 | display: flex;
79 | justify-content: space-between;
80 | padding: 2rem 0;
81 | align-items: flex-start;
82 | font-family: "Syncopate", sans-serif;
83 | font-size: 1.5rem;
84 | text-transform: lowercase;
85 | }
86 |
87 | .links,
88 | .blog-link {
89 | font-weight: 700;
90 | }
91 |
92 | .links a,
93 | .blog-link a {
94 | color: var(--color-dark);
95 | position: relative;
96 | }
97 |
98 | .links a::after,
99 | .blog-link a::after {
100 | content: "";
101 | width: 0%;
102 | height: 0.2rem;
103 | position: absolute;
104 | top: 50%;
105 | left: 50%;
106 | transform: translate(-50%, -50%);
107 | background-color: var(--color-primary);
108 | transition: 0.5s;
109 | }
110 |
111 | .links a:hover::after,
112 | .blog-link a:hover::after {
113 | width: 120%;
114 | }
115 |
116 | .logo a {
117 | color: var(--color-darker);
118 | }
119 |
120 | /* ********************HERO STYLES******************** */
121 |
122 | .hero {
123 | font-family: "Bai Jamjuree", sans-serif;
124 | text-transform: uppercase;
125 | text-align: center;
126 | font-size: 10vw;
127 | line-height: 1.2;
128 | color: var(--color-darker);
129 | display: flex;
130 | flex-direction: column;
131 | justify-content: center;
132 | min-height: 100vh;
133 | align-items: center;
134 | position: relative;
135 | }
136 |
137 | .ethereal,
138 | .canvas {
139 | position: relative;
140 | }
141 |
142 | .ethereal span,
143 | .canvas span {
144 | position: absolute;
145 | top: 0;
146 | left: 0;
147 | right: 0;
148 | bottom: 0;
149 | width: 100%;
150 | height: 100%;
151 | background-color: var(--color-lighter);
152 | }
153 |
154 | .photos {
155 | position: absolute;
156 | top: 0;
157 | left: 0;
158 | right: 0;
159 | bottom: 0;
160 | z-index: 1;
161 | display: grid;
162 | grid-template-columns: repeat(7, 1fr);
163 | grid-template-rows: repeat(5, 1fr);
164 | }
165 |
166 | .photo {
167 | width: 100%;
168 | height: 100%;
169 | background-position: center;
170 | background-repeat: no-repeat;
171 | background-size: cover;
172 | overflow: hidden;
173 | }
174 |
175 | .photo.one {
176 | grid-column: 1;
177 | grid-row: 2;
178 | }
179 |
180 | .photo.two {
181 | grid-column: 4;
182 | grid-row: 3;
183 | }
184 |
185 | .photo.three {
186 | grid-column: 2;
187 | grid-row: 5;
188 | }
189 |
190 | .photo.four {
191 | grid-column: 7;
192 | grid-row: 4;
193 | }
194 |
195 | .photo.five {
196 | grid-column: 5;
197 | grid-row: 1;
198 | }
199 |
200 | /* *****************Featured STYLES***************** */
201 |
202 | .features {
203 | display: grid;
204 | grid-template-columns: 30% auto;
205 | align-items: center;
206 | gap: 10rem;
207 | }
208 |
209 | .feature-text {
210 | text-transform: uppercase;
211 | letter-spacing: 5px;
212 | font-weight: 500;
213 | }
214 |
215 | .features img {
216 | width: 100%;
217 | display: block;
218 | }
219 |
220 | .feature-left,
221 | .feature-right {
222 | display: grid;
223 | gap: 1rem;
224 | position: relative;
225 | }
226 |
227 | .feature-shutter-left,
228 | .feature-shutter-right {
229 | position: absolute;
230 | z-index: 1;
231 | height: 100%;
232 | width: 100%;
233 | top: 0;
234 | left: 0;
235 | right: 0;
236 | bottom: 0;
237 | background-color: var(--color-lighter);
238 | }
239 |
240 | /* ********************ABOUT STYLES******************** */
241 |
242 | .about p {
243 | font-size: 3vw;
244 | line-height: 1.5;
245 | }
246 |
247 | .about p:last-child {
248 | margin-top: 3vw;
249 | }
250 |
251 | /* ********************GAllERY STYLES******************** */
252 |
253 | .gallery .section-title {
254 | margin-left: 5vw;
255 | }
256 |
257 | .gallery-wrapper {
258 | display: grid;
259 | grid-template-columns: 1fr;
260 | justify-items: center;
261 | gap: 10vw;
262 | padding: 10vw;
263 | background-color: var(--color-primary);
264 | }
265 |
266 | .gallery-item {
267 | position: relative;
268 | width: 50%;
269 | }
270 |
271 | .gallery-item-title {
272 | position: absolute;
273 | top: 10%;
274 | left: -50%;
275 | font-family: "Bai Jamjuree", sans-serif;
276 | font-size: 8vw;
277 | line-height: 1.2;
278 | text-transform: uppercase;
279 | color: var(--color-lighter);
280 | z-index: 1;
281 | mix-blend-mode: color-dodge;
282 | }
283 |
284 | .gallery-item-category {
285 | position: absolute;
286 | left: 0;
287 | bottom: -5%;
288 | text-transform: uppercase;
289 | color: var(--color-lighter);
290 | letter-spacing: 10px;
291 | z-index: 1;
292 | }
293 |
294 | .gallery-item-img {
295 | background-position: center;
296 | background-repeat: no-repeat;
297 | background-size: cover;
298 | width: 100%;
299 | height: 100vh;
300 | }
301 |
302 | /* ********************FOOTER STYLES******************** */
303 |
304 | .footer {
305 | text-align: center;
306 | }
307 |
308 | .footer h1 {
309 | font-family: "Bodoni Moda", serif;
310 | font-size: 10vw;
311 | text-transform: lowercase;
312 | color: var(--color-primary);
313 | z-index: -1;
314 | }
315 |
316 | /* *****************NOT FOUND STYLES***************** */
317 |
318 | .not-found {
319 | display: grid;
320 | grid-template-columns: 1fr 1fr;
321 | align-content: flex-start;
322 | column-gap: 5vw;
323 | row-gap: 1vw;
324 | }
325 |
326 | .headline-1,
327 | .headline-2 {
328 | font-family: "Bai Jamjuree", sans-serif;
329 | font-size: 8vw;
330 | font-weight: 700;
331 | text-transform: capitalize;
332 | line-height: 1;
333 | }
334 |
335 | .img-1,
336 | .img-2 {
337 | width: 20vw;
338 | height: 20vw;
339 | overflow: hidden;
340 | display: flex;
341 | justify-content: center;
342 | align-items: center;
343 | z-index: 1;
344 | }
345 |
346 | .img-1 img,
347 | .img-2 img {
348 | display: block;
349 | width: 100%;
350 | }
351 |
352 | .img-2 {
353 | justify-self: self-end;
354 | }
355 |
356 | /* *********************BLOG STYLES********************* */
357 |
358 | .blog-wrapper {
359 | display: grid;
360 | width: 95%;
361 | margin: 2rem auto;
362 |
363 | grid-template-areas:
364 | " a a d e"
365 | " b c d f"
366 | " g h h k"
367 | " g i j k ";
368 |
369 | gap: 2rem;
370 | }
371 |
372 | .blog-item:nth-child(1) {
373 | grid-area: b;
374 | }
375 |
376 | .blog-item:nth-child(2) {
377 | grid-area: k;
378 | }
379 |
380 | .blog-item:nth-child(3) {
381 | grid-area: i;
382 | }
383 |
384 | .blog-item:nth-child(4) {
385 | grid-area: e;
386 | }
387 |
388 | .blog-item:nth-child(5) {
389 | grid-area: a;
390 | }
391 |
392 | .blog-item:nth-child(12) {
393 | grid-area: j;
394 | }
395 |
396 | .blog-item:nth-child(7) {
397 | grid-area: d;
398 | }
399 |
400 | .blog-item:nth-child(8) {
401 | grid-area: g;
402 | }
403 |
404 | .blog-item:nth-child(9) {
405 | grid-area: c;
406 | }
407 |
408 | .blog-item:nth-child(10) {
409 | grid-area: h;
410 | }
411 |
412 | .blog-item:nth-child(6) {
413 | grid-area: f;
414 | }
415 |
416 | .blog-item {
417 | position: relative;
418 | overflow: hidden;
419 | }
420 |
421 | .blog-img {
422 | width: 100%;
423 | height: 100%;
424 | object-fit: cover;
425 | transition: 0.5s;
426 | }
427 |
428 | .blog-img img {
429 | display: block;
430 | width: 100%;
431 | height: 100%;
432 | transition: all 0.3s ease-in-out;
433 | }
434 |
435 | .blog-item:hover img {
436 | filter: saturate(0) brightness(0.5);
437 | }
438 |
439 | .blog-texts {
440 | position: absolute;
441 | width: 100%;
442 | right: 0;
443 | top: 0;
444 | left: 0;
445 | bottom: -100rem;
446 | background-image: linear-gradient(tobottom, transparent, black);
447 | padding: 2.5rem auto;
448 | transition: 0.5s;
449 | display: flex;
450 | flex-direction: column;
451 | align-items: center;
452 | justify-content: center;
453 | }
454 |
455 | .blog-item:hover .blog-texts {
456 | bottom: 0;
457 | }
458 |
459 | .blog-title {
460 | font-family: "Bodoni Moda", serif;
461 | font-size: 3vw;
462 | text-align: center;
463 | text-transform: capitalize;
464 | color: var(--color-light);
465 | letter-spacing: 0.5rem;
466 | margin-bottom: 15px;
467 | font-weight: 900;
468 | }
469 |
--------------------------------------------------------------------------------