├── public
└── index.html
├── src
├── components
│ ├── Home.js
│ ├── Footer.js
│ ├── About.js
│ ├── GalleryItem.js
│ ├── Gallery.js
│ ├── Featured.js
│ ├── Navbar.js
│ ├── menu.js
│ ├── Hero.js
│ ├── Blog.js
│ └── NotFound.js
├── index.js
├── hooks
│ ├── useSmoothScroll.js
│ └── gsap.js
├── App.js
├── style.css
└── index.css
├── .gitignore
├── README.md
└── package.json
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Immemorial
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/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/components/Footer.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { useFooterHeadLine } from "../hooks/gsap";
3 |
4 | const Footer = () => {
5 | const footerRef = useRef(null);
6 | const footerHeadLine = useRef(null);
7 |
8 | useFooterHeadLine(footerHeadLine, footerRef);
9 | return (
10 |
11 | Bonjour
12 | © {new Date().getFullYear()} Immemorial. Crafted by yours truly
13 |
14 | );
15 | };
16 |
17 | export default Footer;
18 |
--------------------------------------------------------------------------------
/src/hooks/useSmoothScroll.js:
--------------------------------------------------------------------------------
1 | import Lenis from "@studio-freight/lenis";
2 | import { useEffect } from "react";
3 | export const useSmoothScroll = () => {
4 | const lenis = new Lenis({
5 | duration: 1.5,
6 | easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
7 | direction: "vertical", // vertical, horizontal
8 | smooth: true,
9 | });
10 |
11 | useEffect(() => {
12 | function raf(time) {
13 | //raf = request animation frame
14 | lenis.raf(time);
15 | //time ta scrolling are
16 | requestAnimationFrame(raf);
17 | }
18 |
19 | requestAnimationFrame(raf);
20 | }, []);
21 | };
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | What is Immemorial?
2 | Immemorial is a platform that takes users on a journey through the lost treasures and shining stars of the 1990s. It features easy filtering functionality, allowing users to find their favorite TV shows, music albums, cartoons, and more from the 90s. The platform is updated regularly with new and classic favorites from the era.
3 |
4 | What's the only era that never seems to end? The 90s! Journey through appreciating items from 90s TV, music, and art. See if you remember old toys, cartoons, or prints of such. Indulge in some nostalgia before our world falls back into the dark ages.
5 |
6 | How to use this repo?
7 | Download or clone this repo and run the following command in the terminal:
8 |
9 | npm install
10 |
11 |
12 | Tools:
13 | React.js, React Router, GSAP
14 |
15 | Thanks for visiting this repo, take care!
16 |
--------------------------------------------------------------------------------
/src/components/About.js:
--------------------------------------------------------------------------------
1 | const About = () => {
2 | return (
3 |
4 | {" "}
5 | About
6 |
7 | Explore the lost treasures and shining stars of the 1990s! Find your
8 | favorite cartoons, TV shows, music albums, & more with easy filtering
9 | functionality. With Immemorial, stay up-to-date with all your 90s
10 | favorites while turning back time.
11 |
12 |
13 | What's the only era that never seems to end? The 90s! Journey through
14 | appreciating items from 90s TV, music, and art. See if you remember old
15 | toys, cartoons, or prints of such. Indulge in some nostalgia before our
16 | world falls back into the dark ages.
17 |
18 |
19 | );
20 | };
21 |
22 | export default About;
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "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 | const GalleryItem = ({ image }) => {
8 | //ref create
9 | const galleryTitleRef = useRef(null);
10 | const galleryCategoryRef = useRef(null);
11 | const galleryImageRef = useRef(null);
12 | //hook create
13 | useGsapGalleryTitle(galleryTitleRef, galleryImageRef);
14 | useGsapGalleryCategory(galleryCategoryRef, galleryImageRef);
15 | useGsapGalleryImage(galleryImageRef);
16 | return (
17 |
18 |
19 | {image.title}
20 |
21 |
22 | {image.category}
23 |
24 |
29 |
30 | );
31 | };
32 |
33 | export default GalleryItem;
34 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Route, Routes } from "react-router-dom";
3 | import Home from "./components/Home";
4 | // import Navbar from "./components/Navbar";
5 | import Navbar from "./components/Navbar";
6 | import Footer from "./components/Footer";
7 | import { useSmoothScroll } from "./hooks/useSmoothScroll";
8 | import About from "./components/About";
9 | import Gallery from "./components/Gallery";
10 | import Featured from "./components/Featured";
11 | import Blog from "./components/Blog";
12 | import NotFound from "./components/NotFound";
13 | const App = () => {
14 | //smooth scroll are jonno lenis github use kora hoyse
15 | useSmoothScroll();
16 | return (
17 |
18 |
19 |
20 | } />
21 | } />
22 | } />
23 | } />
24 | } />
25 | } />
26 |
27 |
28 |
29 | );
30 | };
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/src/components/Gallery.js:
--------------------------------------------------------------------------------
1 | import GalleryItem from "./GalleryItem";
2 | const images = [
3 | {
4 | id: 1,
5 | src: "https://images.pexels.com/photos/4842487/pexels-photo-4842487.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
6 | title: "Arcade playtime for 90's kids",
7 | category: "Arcade Games",
8 | },
9 | {
10 | id: 2,
11 | src: "https://images.pexels.com/photos/3356608/pexels-photo-3356608.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
12 | title: "No signal - no transmission",
13 | category: "TV",
14 | },
15 | {
16 | id: 3,
17 | src: "https://images.pexels.com/photos/12668238/pexels-photo-12668238.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
18 | title: "Retro Closures",
19 | category: "Boombox",
20 | },
21 | {
22 | id: 4,
23 | src: "https://images.pexels.com/photos/12204293/pexels-photo-12204293.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
24 | title: "Vinyl Loveless Happiness",
25 | category: "Vinyl Record",
26 | },
27 | ];
28 |
29 | const Gallery = () => {
30 | return (
31 |
32 | Gallery
33 |
34 | {images.map((image) => (
35 |
36 | ))}
37 |
38 |
39 | );
40 | };
41 |
42 | export default Gallery;
43 |
--------------------------------------------------------------------------------
/src/components/Featured.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | useGsapFeatureLeftShutterUnveil,
4 | useGsapFeatureRightShutterUnveil,
5 | } from "../hooks/gsap";
6 | const Featured = () => {
7 | const featureRef = useRef(null);
8 | const featureLeftShutterRef = useRef(null);
9 | const featureRightShutterRef = useRef(null);
10 |
11 | useGsapFeatureLeftShutterUnveil(featureLeftShutterRef, featureRef);
12 | useGsapFeatureRightShutterUnveil(featureRightShutterRef, featureRef);
13 | return (
14 |
15 | Featured
16 |
17 |
18 |
90'S TELEPHONE
19 |
23 |
27 |
28 |
29 |
90'S CASSETTE PLAYER
30 |
34 |
38 |
39 |
40 |
41 | );
42 | };
43 |
44 | export default Featured;
45 |
--------------------------------------------------------------------------------
/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { Link } from "react-router-dom";
3 | import { useGsapDownStagger } from "../hooks/gsap";
4 | const Navbar = () => {
5 | const li1 = useRef(null);
6 | const li2 = useRef(null);
7 | const li3 = useRef(null);
8 | const blogRef = useRef(null);
9 | const logoRef = useRef(null);
10 |
11 | const lip1 = useRef(null);
12 | const lip2 = useRef(null);
13 | const lip3 = useRef(null);
14 | const pblogRef = useRef(null);
15 |
16 | const liArrp = [lip1, lip2, lip3];
17 | const pblogArr = [pblogRef];
18 |
19 | const liArr = [li1, li2, li3];
20 | const blogArr = [blogRef];
21 | const logoArr = [logoRef];
22 |
23 | useGsapDownStagger(liArrp, 0.9);
24 | useGsapDownStagger(pblogArr, 1.9);
25 |
26 | useGsapDownStagger(liArr, 0.9);
27 | useGsapDownStagger(logoArr, 1.5);
28 | useGsapDownStagger(blogArr, 1.9);
29 |
30 | return (
31 |
32 |
33 |
34 | Featured
35 |
36 |
37 | About
38 |
39 |
40 | Gallery
41 |
42 |
43 |
44 |
45 |
Immemorial
46 |
47 |
48 |
49 | Blog
50 |
51 |
52 |
53 |
54 | Featured
55 |
56 |
57 | About
58 |
59 |
60 | Gallery
61 |
62 |
63 | Blog
64 |
65 |
66 |
67 | );
68 | };
69 |
70 | export default Navbar;
71 |
--------------------------------------------------------------------------------
/src/components/menu.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import gsap from "gsap";
3 | import "./styles.css"; // Import your CSS file
4 | import { Link } from "react-router-dom";
5 |
6 | const Menu = () => {
7 | const [isMenuOpen, setMenuOpen] = useState(false);
8 |
9 | const toggleMenu = () => {
10 | setMenuOpen(!isMenuOpen);
11 | };
12 |
13 | const closeMenu = () => {
14 | setMenuOpen(false);
15 | };
16 |
17 | const animateMenu = () => {
18 | const tl = gsap.timeline({ paused: true });
19 | tl.to(".menu", {
20 | opacity: 1,
21 | duration: 1,
22 | top: 0,
23 | ease: "Power2.easeInOut",
24 | });
25 | tl.to(
26 | ".nav",
27 | {
28 | opacity: 1,
29 | marginBottom: 0,
30 | duration: 1,
31 | ease: "Power2.easeInOut",
32 | stagger: 0.3,
33 | },
34 | ">-0.5"
35 | );
36 | return tl;
37 | };
38 |
39 | const t1 = animateMenu();
40 |
41 | return (
42 |
43 |
53 |
54 | {/* Menu Stuff */}
55 |
56 |
Menu
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 | Home 01
65 |
66 |
67 |
68 |
69 | Blog 02
70 |
71 |
72 |
73 |
74 | About 03
75 |
76 |
77 |
78 |
79 |
80 | {/* Your other menu content */}
81 |
82 |
83 |
84 | );
85 | };
86 |
87 | export default Menu;
88 |
--------------------------------------------------------------------------------
/src/style.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");
2 |
3 | * {
4 | box-sizing: border-box;
5 | margin: 0;
6 | padding: 0;
7 | font-family: "Poppins", Arial;
8 | }
9 |
10 | body {
11 | background-color: rgb(16, 16, 16);
12 | color: white;
13 | }
14 |
15 | ul {
16 | list-style: none;
17 | }
18 |
19 | .logo {
20 | font-size: 2rem;
21 | }
22 |
23 | .home {
24 | display: flex;
25 | justify-content: space-between;
26 | padding: 50px;
27 | }
28 |
29 | .menu-div {
30 | cursor: pointer;
31 | transition: all 0.2s ease-in-out;
32 | }
33 |
34 | .menu-div:hover {
35 | color: gray;
36 | transition: all 0.2s ease-in-out;
37 | }
38 |
39 | /* Menu Stuff */
40 |
41 | .menu {
42 | opacity: 0.3;
43 | width: 100%;
44 | height: 100vh;
45 | display: flex;
46 | flex-direction: row;
47 | justify-content: space-between;
48 | padding: 0px 50px;
49 | position: fixed;
50 | top: -100%;
51 | align-items: center;
52 | background-color: rgb(39, 39, 39);
53 | }
54 |
55 | .nav {
56 | opacity: 0;
57 | margin-bottom: -20px;
58 | }
59 |
60 | .nav-link {
61 | color: white;
62 | text-decoration: none;
63 | font-size: 3rem;
64 | transition: all 0.2s ease-in-out;
65 | }
66 |
67 | .nav-link:hover {
68 | color: gray;
69 | transition: all 0.2s ease-in-out;
70 | }
71 |
72 | .background {
73 | position: absolute;
74 | font-size: 15rem;
75 | font-weight: 600;
76 | color: rgba(235, 235, 235, 0.04);
77 | user-select: none;
78 | z-index: 1;
79 | }
80 |
81 | .small-number {
82 | font-size: 1.2rem;
83 | }
84 |
85 | .exit {
86 | cursor: pointer;
87 | position: absolute;
88 | right: 40px;
89 | top: 40px;
90 | }
91 |
92 | .title {
93 | font-size: 2rem;
94 | color: rgb(170, 151, 126);
95 | }
96 |
97 | .right {
98 | padding-right: 100px;
99 | }
100 |
101 | .information {
102 | margin-bottom: 24px;
103 | }
104 |
105 | .menu-container {
106 | z-index: 3;
107 | }
108 |
109 | .social-medias > a {
110 | color: white;
111 | text-decoration: none;
112 | text-transform: uppercase;
113 | font-size: 0.9rem;
114 | letter-spacing: 1px;
115 | }
116 |
117 | /* Media Query */
118 | @media screen and (max-width: 660px) {
119 | .menu {
120 | flex-direction: column;
121 | justify-content: center;
122 | }
123 |
124 | .right {
125 | display: none;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/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 | const heroRef = useRef(null);
10 | const shutter1 = useRef(null);
11 | const shutter2 = useRef(null);
12 |
13 | const photo1Ref = useRef(null);
14 | const photo2Ref = useRef(null);
15 | const photo3Ref = useRef(null);
16 | const photo4Ref = useRef(null);
17 | const photo5Ref = useRef(null);
18 |
19 | const photoArr = [photo1Ref, photo2Ref, photo3Ref, photo4Ref, photo5Ref];
20 |
21 | useGsapShutterUnveil(shutter1, 0, heroRef);
22 | useGsapShutterUnveil(shutter2, 0.2, heroRef);
23 | useGsapPhotoDropping(photoArr);
24 | useGsapPhotoLevitate(photoArr, heroRef);
25 |
26 | return (
27 |
28 |
29 | Ethereal
30 |
31 |
32 | Canvas
33 |
34 |
35 |
43 |
44 |
52 |
53 |
61 |
62 |
70 |
78 |
79 |
80 | );
81 | };
82 |
83 | export default Hero;
84 |
--------------------------------------------------------------------------------
/src/components/Blog.js:
--------------------------------------------------------------------------------
1 | const Blog = ({ needFullHight }) => {
2 | return (
3 |
4 | blog
5 |
6 |
7 |
14 |
21 |
28 |
35 |
42 |
49 |
56 |
57 |
58 | );
59 | };
60 |
61 | export default Blog;
62 |
--------------------------------------------------------------------------------
/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | // const NotFound = () => {
2 | // return (
3 | //
4 | //
5 | //
6 | //
7 | //
8 | // Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi,
9 | // repudiandae!
10 | //
11 | //
12 | //
13 | // Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi,
14 | // repudiandae!
15 | //
16 | //
17 | // Lorem ipsum dolor sit amet consectetur adipisicing elit. Animi,
18 | // repudiandae!
19 | //
20 | //
21 | //
22 | //
23 | // Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum
24 | // vitae inventore incidunt eligendi soluta. Vero, laboriosam facere in
25 | // velit rerum libero repellat quidem animi nam voluptatem laborum
26 | // eveniet delectus assumenda.
27 | //
28 | //
29 |
30 | //
Lorem ipsum dolor sit amet.
31 | //
32 | //
33 | // Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti
34 | // pariatur impedit obcaecati eos consequatur esse sequi quasi,
35 | // incidunt itaque temporibus.
36 | //
37 | //
38 | // Lorem ipsum dolor sit amet, consectetur adipisicing elit. Deleniti
39 | // pariatur impedit obcaecati eos consequatur esse sequi quasi,
40 | // incidunt itaque temporibus.
41 | //
42 | //
43 | //
44 | //
45 | // );
46 | // };
47 |
48 | // export default NotFound;
49 |
50 | import { useRef } from "react";
51 | import { useGsapNotFoundHeadline, useGsapNotFoundImg } from "../hooks/gsap";
52 |
53 | const NotFound = () => {
54 | const leftHeadlineRef = useRef(null);
55 | const rightHeadlineRef = useRef(null);
56 | const leftImgRef = useRef(null);
57 | const rightImgRef = useRef(null);
58 |
59 | useGsapNotFoundHeadline(leftHeadlineRef);
60 | useGsapNotFoundHeadline(rightHeadlineRef, "100vw");
61 | useGsapNotFoundImg(leftImgRef);
62 | useGsapNotFoundImg(rightImgRef);
63 |
64 | return (
65 |
66 |
67 | Sorry, we couldn't
68 |
69 |
70 |
74 |
75 |
76 |
80 |
81 |
82 | Find that page
83 |
84 |
85 | );
86 | };
87 |
88 | export default NotFound;
89 |
--------------------------------------------------------------------------------
/src/hooks/gsap.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import gsap, { Expo } from "gsap";
3 | import ScrollTrigger from "gsap/ScrollTrigger";
4 |
5 | gsap.registerPlugin(ScrollTrigger);
6 | //hero likha animation
7 | export const useGsapShutterUnveil = (item, delay = 0, trigger) => {
8 | //heroRef trigger hisabe use kora hoase akhane
9 | //trigger bolte animate jayga ta bojhai....oi jayga tekhe bar hoa abar fire gele animate hoi
10 | useEffect(() => {
11 | const el = item.current;
12 | gsap.fromTo(
13 | el,
14 | {
15 | height: "100%",
16 | },
17 | {
18 | height: 0,
19 | duration: 2,
20 | ease: Expo.easeInOut,
21 | delay: delay,
22 | scrollTrigger: {
23 | trigger: trigger.current,
24 | //view port a dhukle animation play hobe bar hole off hobe aitar jonno toggleAction
25 | toggleActions: "play reverse play reverse",
26 | },
27 | }
28 | );
29 | }, [item, delay, trigger]);
30 | };
31 |
32 | //navbar animation
33 | export const useGsapDownStagger = (arr, delay = 0) => {
34 | useEffect(() => {
35 | const el = arr.map((item) => item.current);
36 | gsap.fromTo(
37 | el,
38 | {
39 | y: "-100%",
40 | opacity: 0,
41 | },
42 | {
43 | y: 0,
44 | opacity: 1,
45 | //koto time dhore animate hobe tar jonno duration
46 | duration: 1.2,
47 | //array item gula kotokhon por por ashbe tar jonno stagger set kora hoi
48 | stagger: 0.2,
49 | ease: Expo.easeIn,
50 | delay: delay,
51 | }
52 | );
53 | }, [arr, delay]);
54 | };
55 |
56 | //hero Image drop animation
57 |
58 | export const useGsapPhotoDropping = (arr) => {
59 | useEffect(() => {
60 | const el = arr.map((item) => item.current);
61 | gsap.fromTo(
62 | el,
63 | {
64 | y: "-100vh",
65 | scale: 0,
66 | },
67 | {
68 | y: 0,
69 | //pic soto tekhe boro hoar jonno scale use kora
70 | scale: 1,
71 | duration: 1.7,
72 | stagger: 0.2,
73 | ease: Expo.easeIn,
74 | delay: 2,
75 | }
76 | );
77 | }, [arr]);
78 | };
79 |
80 | export const useGsapPhotoLevitate = (arr, trigger) => {
81 | useEffect(() => {
82 | const el = arr.map((item) => item.current);
83 | gsap.fromTo(
84 | el,
85 | {
86 | y: 0,
87 | },
88 | {
89 | y: "-100%",
90 | ease: Expo.easeInOut,
91 |
92 | scrollTrigger: {
93 | trigger: trigger.current,
94 | //scrub value 1 mane true
95 | scrub: 1,
96 | toggleActions: "play reverse play reverse",
97 | },
98 | }
99 | );
100 | }, [arr, trigger]);
101 | };
102 |
103 | export const useGsapFeatureLeftShutterUnveil = (item, trigger) => {
104 | useEffect(() => {
105 | const el = item.current;
106 | gsap.fromTo(
107 | el,
108 | {
109 | height: "100%",
110 | },
111 | {
112 | height: 0,
113 | duration: 1.3,
114 | ease: Expo.easeInOut,
115 | scrollTrigger: {
116 | trigger: trigger.current,
117 | start: "top center",
118 | end: "bottom center",
119 | toggleActions: "play reverse play reverse",
120 | },
121 | }
122 | );
123 | }, [item, trigger]);
124 | };
125 |
126 | export const useGsapFeatureRightShutterUnveil = (item, trigger) => {
127 | useEffect(() => {
128 | const el = item.current;
129 | gsap.fromTo(
130 | el,
131 | {
132 | width: "100%",
133 | },
134 | {
135 | width: 0,
136 | duration: 1.3,
137 | ease: Expo.easeInOut,
138 | scrollTrigger: {
139 | trigger: trigger.current,
140 | start: "top center",
141 | end: "bottom center",
142 | toggleActions: "play reverse play reverse",
143 | },
144 | }
145 | );
146 | }, [item, trigger]);
147 | };
148 |
149 | export const useGsapGalleryImage = (item) => {
150 | useEffect(() => {
151 | const el = item.current;
152 | gsap.fromTo(
153 | el,
154 | {
155 | x: 0,
156 | width: 0,
157 | },
158 | {
159 | x: "30%",
160 | width: "100%",
161 | ease: Expo.easeInOut,
162 | scrollTrigger: {
163 | trigger: el,
164 | start: "top center",
165 | end: "bottom top",
166 | toggleActions: "play reverse play reverse",
167 | },
168 | }
169 | );
170 | }, [item]);
171 | };
172 | export const useGsapGalleryTitle = (item, trigger) => {
173 | useEffect(() => {
174 | const el = item.current;
175 | gsap.fromTo(
176 | el,
177 | {
178 | x: "30%",
179 | },
180 | {
181 | x: 0,
182 | ease: Expo.easeInOut,
183 | scrollTrigger: {
184 | trigger: trigger.current,
185 | start: "top center",
186 | end: "bottom top",
187 | toggleActions: "play reverse play reverse",
188 | },
189 | }
190 | );
191 | }, [item, trigger]);
192 | };
193 |
194 | export const useGsapGalleryCategory = (item, trigger) => {
195 | useEffect(() => {
196 | const el = item.current;
197 | gsap.fromTo(
198 | el,
199 | {
200 | x: "-100vw",
201 | },
202 | {
203 | x: "0",
204 |
205 | ease: Expo.easeInOut,
206 | scrollTrigger: {
207 | trigger: trigger.current,
208 | start: "top center",
209 | end: "bottom top",
210 | toggleActions: "play reverse play reverse",
211 | },
212 | }
213 | );
214 | }, [item, trigger]);
215 | };
216 |
217 | export const useFooterHeadLine = (item, trigger) => {
218 | useEffect(() => {
219 | const el = item.current;
220 | gsap.fromTo(
221 | el,
222 | {
223 | y: "-100%",
224 | },
225 | {
226 | y: 0,
227 | duration: 1,
228 | ease: Expo.easeInOut,
229 | scrollTrigger: {
230 | trigger: trigger.current,
231 | toggleActions: "play",
232 | },
233 | }
234 | );
235 | }, [item, trigger]);
236 | };
237 |
238 | export const useGsapNotFoundHeadline = (item, vw = "-100vw") => {
239 | useEffect(() => {
240 | const el = item.current;
241 |
242 | gsap.fromTo(
243 | el,
244 | {
245 | x: vw,
246 | },
247 | {
248 | x: 0,
249 | duration: 1.5,
250 | ease: Expo.easeInOut,
251 | }
252 | );
253 | }, [item, vw]);
254 | };
255 |
256 | export const useGsapNotFoundImg = (item) => {
257 | useEffect(() => {
258 | const el = item.current;
259 |
260 | gsap.fromTo(
261 | el,
262 | {
263 | scale: 0,
264 | borderRadius: "50%",
265 | },
266 | {
267 | scale: 1,
268 | borderRadius: 0,
269 | duration: 4,
270 | delay: 1,
271 | ease: "elastic",
272 | }
273 | );
274 | }, [item]);
275 | };
276 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
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 | /* Bai Jamjuree */
4 | @import url("https://fonts.googleapis.com/css2?family=Bai+Jamjuree:wght@200;300;400;500;600;700&display=swap");
5 | /* Syncopate */
6 | @import url("https://fonts.googleapis.com/css2?family=Syncopate:wght@400;700&display=swap");
7 | /* Bodoni Moda */
8 | @import url("https://fonts.googleapis.com/css2?family=Bodoni+Moda:wght@6400;500;600;700;800;900&display=swap");
9 | :root {
10 | --color-primary: #d53f41;
11 | --color-dark: #0c0b0b;
12 | --color-darker: #131212;
13 | --color-light: #dbd8d6;
14 | --color-lighter: #f5f0ec;
15 | }
16 | * {
17 | margin: 0;
18 | padding: 0;
19 | list-style: none;
20 | text-decoration: none;
21 | box-sizing: border-box;
22 | }
23 | html,
24 | body {
25 | overflow-x: hidden;
26 | }
27 | html {
28 | font-size: 62.5%;
29 | }
30 | body {
31 | font-family: "poppins", sans-serif;
32 | font-size: 2rem;
33 | font-weight: 400;
34 | line-height: 1.7;
35 | letter-spacing: 1px;
36 | background-color: var(--color-lighter);
37 | }
38 | .wrapper {
39 | margin: 0 5vw;
40 | }
41 | section {
42 | padding: 8vw 0;
43 | }
44 | .section-title {
45 | font-family: "syncopate", sans-serif;
46 | font-size: 1.5rem;
47 | color: var(--color-primary);
48 | padding-bottom: 5vw;
49 | text-transform: lowercase;
50 | }
51 | .min-h-100vh {
52 | min-height: 100vh;
53 | }
54 | .navbar {
55 | display: flex;
56 | justify-content: space-between;
57 | align-items: flex-start;
58 | padding: 6rem 0;
59 | font-family: "syncopate", sans-serif;
60 | font-size: 1.5rem;
61 | text-transform: lowercase;
62 | }
63 | .phone-menu {
64 | display: none;
65 | }
66 | .links,
67 | .blog-link {
68 | font-weight: 700;
69 | display: block;
70 | }
71 | .links a,
72 | .blog-link a {
73 | color: var(--color-dark);
74 | position: relative;
75 | }
76 | .links a::after,
77 | .blog-link a::after {
78 | content: "";
79 | width: 0;
80 | height: 0.2rem;
81 | position: absolute;
82 | top: 30%;
83 | left: 50%;
84 | transform: translate(-30%, 50%);
85 | background-color: var(--color-primary);
86 | transition: 0.5s;
87 | }
88 | .links a:hover:after,
89 | .blog-link a:hover:after {
90 | width: 120%;
91 | }
92 |
93 | .logo a {
94 | color: var(--color-darker);
95 | }
96 | .hero {
97 | font-family: "Bai Jamjuree", sans-serif;
98 | text-transform: uppercase;
99 | text-align: center;
100 | font-size: 10vw;
101 | line-height: 1.2;
102 | min-height: 100vh;
103 | color: var(--color-darker);
104 | position: relative;
105 | flex-direction: column;
106 | justify-content: center;
107 | display: flex;
108 | align-items: center;
109 | }
110 | .ethereal,
111 | .canvas {
112 | position: relative;
113 | }
114 | .ethereal span,
115 | .canvas span {
116 | position: absolute;
117 | top: 0;
118 | left: 0;
119 | right: 0;
120 | bottom: 0;
121 | width: 100%;
122 | height: 100%;
123 | background-color: var(--color-lighter);
124 | }
125 | .photos {
126 | position: absolute;
127 | top: 0;
128 | left: 0;
129 | bottom: 0;
130 | right: 0;
131 | z-index: 1;
132 | display: grid;
133 | grid-template-columns: repeat(7, 1fr);
134 | grid-template-rows: repeat(5, 1fr);
135 | }
136 | .photo {
137 | width: 100%;
138 | height: 100%;
139 | background-position: center;
140 | background-repeat: no-repeat;
141 | background-size: cover;
142 | object-fit: cover;
143 | }
144 | .photo.one {
145 | grid-column: 5;
146 | grid-row: 1;
147 | }
148 | .photo.two {
149 | grid-column: 1;
150 | grid-row: 2;
151 | }
152 | .photo.three {
153 | grid-column: 4;
154 | grid-row: 3;
155 | }
156 | .photo.four {
157 | grid-column: 7;
158 | grid-row: 4;
159 | }
160 | .photo.five {
161 | grid-column: 2;
162 | grid-row: 5;
163 | }
164 |
165 | .features {
166 | display: grid;
167 | grid-template-columns: 30% auto;
168 | align-items: center;
169 | gap: 7rem;
170 | }
171 | .features img {
172 | width: 100%;
173 | }
174 | .feature-l,
175 | .feature-r {
176 | display: flex;
177 | flex-direction: column;
178 | gap: 1rem;
179 | position: relative;
180 | }
181 |
182 | .feature-text {
183 | letter-spacing: 5px;
184 | font-weight: 500;
185 | }
186 | .feature-shutter-l,
187 | .feature-shutter-r {
188 | position: absolute;
189 | z-index: 1;
190 | width: 100%;
191 | height: 100%;
192 | background-color: var(--color-lighter);
193 | top: 0;
194 | left: 0;
195 | right: 0;
196 | bottom: 0;
197 | }
198 |
199 | .about p:last-child {
200 | margin-top: 3vw;
201 | }
202 | .about p {
203 | font-size: 3vw;
204 | line-height: 1.5;
205 | color: var(--color-darker);
206 | }
207 | .gallery > .section-title {
208 | margin-left: 5vw;
209 | }
210 |
211 | .gallery-wrapper {
212 | display: grid;
213 | grid-template-columns: 1fr;
214 | justify-items: center;
215 | gap: 10vw;
216 | padding: 10vw;
217 | background-color: var(--color-primary);
218 | }
219 | .gallery-item {
220 | position: relative;
221 | width: 50%;
222 | }
223 | .gallery-item-title {
224 | position: absolute;
225 | top: 10%;
226 | left: -50%;
227 | font-family: "Bai Jamjuree", sans-serif;
228 | font-size: 8vw;
229 | line-height: 1.2;
230 | text-transform: uppercase;
231 | color: var(--color-lighter);
232 | z-index: 1;
233 | mix-blend-mode: color-dodge;
234 | }
235 | .gallery-item-category {
236 | position: absolute;
237 | left: 0;
238 | bottom: -6%;
239 | text-transform: uppercase;
240 | color: var(--color-lighter);
241 | letter-spacing: 10px;
242 | z-index: 1;
243 | }
244 | .gallery-item-img {
245 | background-position: center;
246 | background-repeat: no-repeat;
247 | background-size: cover;
248 | width: 100%;
249 | height: 100vh;
250 | }
251 | /* not found */
252 | .not-found {
253 | min-height: 100vh;
254 | display: grid;
255 | grid-template-columns: 1fr 1fr;
256 | align-content: flex-start;
257 | column-gap: 5vw;
258 | row-gap: 1vw;
259 | padding: 5vw 0;
260 | }
261 |
262 | .headline-1,
263 | .headline-2 {
264 | font-family: "Bai Jamjuree", sans-serif;
265 | font-size: 8vw;
266 | font-weight: 700;
267 | line-height: 1;
268 | text-transform: capitalize;
269 | }
270 |
271 | .img-1,
272 | .img-2 {
273 | width: 20vw;
274 | height: 20vw;
275 | overflow: hidden;
276 | display: flex;
277 | justify-content: center;
278 | align-items: center;
279 | z-index: 1;
280 | }
281 |
282 | .img-1 img,
283 | .img-2 img {
284 | display: block;
285 | width: 100%;
286 | }
287 |
288 | .img-2 {
289 | justify-self: self-end;
290 | }
291 |
292 | .footer {
293 | text-align: center;
294 | }
295 | .footer h1 {
296 | font-family: "Bodoni Moda", serif;
297 | font-size: 10vw;
298 | text-transform: lowercase;
299 | color: var(--color-primary);
300 | }
301 |
302 | .blog-area {
303 | display: grid;
304 | height: 100vh;
305 | grid-template-columns: repeat(auto-fit, minmax(90px, 1fr));
306 | gap: 1rem;
307 | grid-auto-flow: dense;
308 | }
309 | .blog-image-1 {
310 | grid-column: auto / span 3;
311 | grid-row: auto / span 2;
312 | }
313 | .blog-image-2 {
314 | grid-column: auto / span 3;
315 | }
316 | .blog-image-3 {
317 | grid-column: auto / span 2;
318 | grid-row: auto / span 3;
319 | }
320 | .blog-image-4 {
321 | grid-column: auto / span 3;
322 | }
323 | .blog-image-5 {
324 | grid-column: auto / span 3;
325 | }
326 | .blog-image-6 {
327 | grid-column: auto / span 3;
328 | grid-row: auto / span 2;
329 | }
330 | .blog-image-7 {
331 | grid-column: auto / span 6;
332 | }
333 | @media only screen and (max-width: 688px) {
334 | .navbar {
335 | font-size: 1.1rem;
336 | }
337 | .links,
338 | .blog-link {
339 | display: none;
340 | }
341 | .phone-menu {
342 | display: block;
343 | }
344 | .blog-link,
345 | .phone-menu > li > a {
346 | color: var(--color-dark);
347 | font-weight: bold;
348 | }
349 | .photos {
350 | position: absolute;
351 | top: 0;
352 | left: 0;
353 | bottom: 0;
354 | right: 0;
355 | z-index: 1;
356 | display: grid;
357 | grid-template-columns: repeat(4, 1fr);
358 | grid-template-rows: repeat(5, 1fr);
359 | }
360 | .photo {
361 | width: 100%;
362 | height: 100%;
363 | background-position: center;
364 | background-repeat: no-repeat;
365 | background-size: cover;
366 | object-fit: cover;
367 | }
368 | .photo.one {
369 | grid-column: 4;
370 | grid-row: 1;
371 | }
372 | .photo.two {
373 | grid-column: 1;
374 | grid-row: 2;
375 | }
376 | .photo.three {
377 | grid-column: 3;
378 | grid-row: 3;
379 | }
380 | .photo.four {
381 | grid-column: 4;
382 | grid-row: 4;
383 | display: none;
384 | }
385 | .photo.five {
386 | grid-column: 2;
387 | grid-row: 5;
388 | }
389 | .about p {
390 | font-size: 5vw;
391 | line-height: 1.5;
392 | color: var(--color-darker);
393 | }
394 | .gallery-item-img {
395 | height: 30vh;
396 | }
397 | }
398 | @media only screen and (max-width: 400px) {
399 | .navbar {
400 | padding: 2rem 0 0 0;
401 | }
402 | .photos {
403 | position: absolute;
404 | top: 0;
405 | left: 0;
406 | bottom: 0;
407 | right: 0;
408 | z-index: 1;
409 | display: grid;
410 | grid-template-columns: repeat(3, 1fr);
411 | grid-template-rows: repeat(5, 1fr);
412 | }
413 | .photo {
414 | width: 100%;
415 | height: 100%;
416 | background-position: center;
417 | background-repeat: no-repeat;
418 | background-size: cover;
419 | object-fit: cover;
420 | }
421 | .photo.one {
422 | grid-column: 4;
423 | grid-row: 1;
424 | display: none;
425 | }
426 | .photo.two {
427 | grid-column: 1;
428 | grid-row: 2;
429 | }
430 | .photo.three {
431 | grid-column: 3;
432 | grid-row: 3;
433 | }
434 | .photo.four {
435 | grid-column: 3;
436 | grid-row: 4;
437 | display: none;
438 | }
439 | .photo.five {
440 | grid-column: 2;
441 | grid-row: 5;
442 | }
443 | }
444 |
--------------------------------------------------------------------------------