├── public
└── index.html
├── src
├── components
│ ├── Home.js
│ ├── Home.jsx
│ ├── SectionTitle.js
│ ├── Footer.js
│ ├── Favourites.js
│ ├── Blog.js
│ ├── Hero.jsx
│ ├── About.js
│ ├── GalleryItem.js
│ ├── Navbar.jsx
│ ├── Navbar.js
│ ├── Gallery.js
│ ├── NotFound.js
│ ├── Featured.js
│ └── Hero.js
├── index.js
├── Hooks
│ ├── useSmothScroll.js
│ ├── useSmoothScroll.js
│ └── gsap.js
├── App.js
├── data
│ └── blogs.js
└── index.css
├── .gitignore
├── package.json
└── README.md
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Immorial Website
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import Hero from "./Hero";
2 | import Featured from "./Featured";
3 | import About from "./About";
4 | import Gallery from "./Gallery";
5 |
6 | const Home = () => {
7 | return (
8 | <>
9 |
10 |
11 |
12 |
13 | >
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/Home.jsx:
--------------------------------------------------------------------------------
1 | import Hero from './Hero';
2 | import Navbar from './Navbar';
3 |
4 | const Home = () => {
5 | return (
6 |
7 |
8 |
9 |
10 | );
11 | };
12 |
13 | export default Home;
--------------------------------------------------------------------------------
/src/Hooks/useSmothScroll.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import Lenis from '@studio-freight/lenis'
3 |
4 | export const useSmothScroll = () => {
5 | const lenis = new Lenis({
6 | duration: 1.2,
7 | easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
8 | direction: 'vertical',
9 | smooth: true,
10 | })
11 | useEffect(() => {
12 | function raf(time) {
13 | lenis.raf(time)
14 | requestAnimationFrame(raf)
15 | }
16 | requestAnimationFrame(raf)
17 | }, [])
18 | }
--------------------------------------------------------------------------------
/src/Hooks/useSmoothScroll.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import Lenis from "@studio-freight/lenis";
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 | smooth: true,
10 | });
11 |
12 | useEffect(() => {
13 | function raf(time) {
14 | lenis.raf(time);
15 | requestAnimationFrame(raf);
16 | }
17 |
18 | requestAnimationFrame(raf);
19 | }, []);
20 | };
21 |
--------------------------------------------------------------------------------
/src/components/SectionTitle.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { useGsapLeftWalking } from "../hooks/gsap";
3 |
4 | const SectionTitle = ({ title, needMargin = false }) => {
5 | const secTitle = useRef(null);
6 |
7 | useGsapLeftWalking(secTitle);
8 |
9 | const optionalMarginStyles = {
10 | margin: needMargin ? "0 5vw" : null,
11 | };
12 |
13 | return (
14 |
15 | {title}
16 |
17 | );
18 | };
19 |
20 | export default SectionTitle;
21 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { useGsapFooterHeadline } from "../hooks/gsap";
3 |
4 | const Footer = () => {
5 | const footerRef = useRef(null);
6 | const footerHeadline = useRef(null);
7 |
8 | useGsapFooterHeadline(footerHeadline, footerRef);
9 |
10 | return (
11 |
12 | Bonjour
13 |
14 | © {new Date().getFullYear()} Immemorial. Crafted by yours truly
15 |
16 |
17 | );
18 | };
19 |
20 | export default Footer;
21 |
--------------------------------------------------------------------------------
/src/components/Favourites.js:
--------------------------------------------------------------------------------
1 | import SectionTitle from "./SectionTitle";
2 | import Blog from "./Blog";
3 | import { blogsArr } from "../data/blogs";
4 |
5 | const Favourites = ({ minHeight }) => {
6 | return (
7 |
11 |
12 |
13 | {blogsArr.map((blog) => (
14 |
15 | ))}
16 |
17 |
18 | );
19 | };
20 |
21 | export default Favourites;
22 |
--------------------------------------------------------------------------------
/src/components/Blog.js:
--------------------------------------------------------------------------------
1 | const Blog = ({ blog }) => {
2 | return (
3 |
4 |
5 |
6 |
7 |
8 |
{blog.title}
9 |
10 | Published by {blog.author} on{" "}
11 | {blog.date}
12 |
13 |
{blog.body}
14 |
Read more
15 |
16 |
17 | );
18 | };
19 |
20 | export default Blog;
21 |
--------------------------------------------------------------------------------
/src/components/Hero.jsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from 'react';
2 | import { useGsapHeroTextUpStagger } from '../Hooks/gsap';
3 |
4 | const Hero = () => {
5 | const heroRef = useRef(null);
6 | const heroText1Ref = useRef(null);
7 | const heroText2Ref = useRef(null);
8 | const heroBtnRef = useRef(null);
9 | const heroTextArr = [heroText1Ref, heroText2Ref, heroBtnRef];
10 |
11 | useGsapHeroTextUpStagger(heroTextArr, 1.5);
12 |
13 | return (
14 |
15 |
16 |
17 | war is
18 |
19 |
20 | Coming !
21 |
22 | Be Ready
23 |
24 |
25 | );
26 | };
27 |
28 | export default Hero;
--------------------------------------------------------------------------------
/src/components/About.js:
--------------------------------------------------------------------------------
1 | import SectionTitle from "./SectionTitle";
2 |
3 | const About = ({ minHeight }) => {
4 | return (
5 |
9 |
10 |
11 | Explore the lost treasures and shining stars of the 1990s! Find your
12 | favorite cartoons, TV shows, music albums, & more with easy filtering
13 | functionality. With Immemorial, stay up-to-date with all your 90s
14 | favorites while turning back time.
15 |
16 |
17 | What's the only era that never seems to end? The 90s! Journey through
18 | appreciating items from 90s TV, music, and art. See if you remember old
19 | toys, cartoons, or prints of such. Indulge in some nostalgia before our
20 | world falls back into the dark ages.
21 |
22 |
23 | );
24 | };
25 |
26 | export default About;
27 |
--------------------------------------------------------------------------------
/src/components/GalleryItem.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | useGsapGalleryImg,
4 | useGsapGalleryTitle,
5 | useGsapGalleryCategory,
6 | } from "../hooks/gsap";
7 |
8 | const GalleryItem = ({ src, title, category }) => {
9 | const galleryImg = useRef(null);
10 | const galleryTitle = useRef(null);
11 | const galleryCategory = useRef(null);
12 |
13 | useGsapGalleryImg(galleryImg);
14 | useGsapGalleryTitle(galleryTitle, galleryImg);
15 | useGsapGalleryCategory(galleryCategory, galleryImg);
16 |
17 | return (
18 |
19 |
20 | {title}
21 |
22 |
23 | {category}
24 |
25 |
30 |
31 | );
32 | };
33 |
34 | export default GalleryItem;
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "immorial-react",
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.5",
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/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import {useRef} from 'react';
2 | import { Link } from 'react-router-dom';
3 | import { useGsapDownStagger } from '../Hooks/gsap'
4 |
5 | const Navbar = () => {
6 | const li1 = useRef(null);
7 | const li2 = useRef(null);
8 | const li3 = useRef(null);
9 | const logoRef = useRef(null);
10 |
11 | const liArr = [li1, li2, li3];
12 | const logoArr = [logoRef];
13 |
14 | useGsapDownStagger(liArr, 0.8);
15 | useGsapDownStagger(logoArr, 1.5);
16 |
17 | return (
18 |
19 |
20 |
21 |
22 |
armour
23 |
24 |
25 |
26 |
27 | Featured
28 |
29 |
30 | About
31 |
32 |
33 | Gallery
34 |
35 |
36 |
37 |
38 |
39 | );
40 | };
41 |
42 | export default Navbar;
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom";
2 | import { useSmoothScroll } from "./hooks/useSmoothScroll";
3 | import Navbar from "./components/Navbar";
4 | import Home from "./components/Home";
5 | import Featured from "./components/Featured";
6 | import About from "./components/About";
7 | import Gallery from "./components/Gallery";
8 | import Favourites from "./components/Favourites";
9 | import NotFound from "./components/NotFound";
10 | import Footer from "./components/Footer";
11 |
12 | const App = () => {
13 | const minHeight = true;
14 | useSmoothScroll();
15 |
16 | return (
17 | <>
18 |
19 |
20 | } />
21 | } />
22 | } />
23 | } />
24 | }
27 | />
28 | } />
29 |
30 |
31 | >
32 | );
33 | };
34 |
35 | export default App;
36 |
--------------------------------------------------------------------------------
/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { Link } from "react-router-dom";
3 | import { useGsapDownStagger, useGsapUpward } from "../hooks/gsap";
4 |
5 | const Navbar = () => {
6 | const li1 = useRef(null);
7 | const li2 = useRef(null);
8 | const li3 = useRef(null);
9 | const fav = useRef(null);
10 | const logo = useRef(null);
11 |
12 | const liArr = [li1, li2, li3];
13 | const favArr = [fav];
14 |
15 | useGsapDownStagger(liArr, 1.5);
16 | useGsapDownStagger(favArr, 2.6);
17 | useGsapUpward(logo, 2.2);
18 |
19 | return (
20 |
21 |
22 |
23 | Featured
24 |
25 |
26 | About
27 |
28 |
29 | Gallery
30 |
31 |
32 |
33 |
34 |
Immemorial
35 |
36 |
37 |
38 | Favourites
39 |
40 |
41 | );
42 | };
43 |
44 | export default Navbar;
45 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Immemorial Project
2 |
3 | This is a photography website built with React JS, React Router, Tailwind CSS, GSAP animation, and GSAP ScrollTrigger. The website provides a seamless browsing experience for our users, with a beautiful and responsive design that showcases our photography in the best possible way.
4 |
5 | ## Project Features
6 |
7 | - Responsive and adaptive design using Tailwind CSS
8 | - Dynamic scrolling effects using GSAP ScrollTrigger
9 | - Beautiful animations using GSAP animation
10 | - Seamless browsing experience with React Router
11 |
12 | ## Installation
13 |
14 | 1. Clone the repository to your local machine.
15 | 2. Navigate to the root directory of the project in your terminal.
16 | 3. Run `npm install` to install the project dependencies.
17 | 4. Run `npm start` to start the development server.
18 | 5. Open your web browser and go to `http://localhost:3000` to view the website.
19 |
20 | ## Tools
21 |
22 | 1. react js.
23 | 2. react-router.
24 | 3. tailwind css.
25 | 4. gsap animation.
26 | 5. gsap smooth-scroolTrigger.
27 |
28 | ## Conclusion
29 |
30 | We hope you enjoy exploring our photography website! We're proud of the project and the technologies used to build it. If you have any questions or feedback, please don't hesitate to contact us. Thank you for visiting our website!
31 |
32 |
--------------------------------------------------------------------------------
/src/components/Gallery.js:
--------------------------------------------------------------------------------
1 | import SectionTitle from "./SectionTitle";
2 | import GalleryItem from "./GalleryItem";
3 |
4 | const images = [
5 | {
6 | id: 1,
7 | src: "https://images.pexels.com/photos/4842487/pexels-photo-4842487.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
8 | title: "Arcade playtime for 90's kids",
9 | category: "Arcade Games",
10 | },
11 | {
12 | id: 2,
13 | src: "https://images.pexels.com/photos/3356608/pexels-photo-3356608.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
14 | title: "No signal - no transmission",
15 | category: "TV",
16 | },
17 | {
18 | id: 3,
19 | src: "https://images.pexels.com/photos/12668238/pexels-photo-12668238.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
20 | title: "Retro Closures",
21 | category: "Boombox",
22 | },
23 | {
24 | id: 4,
25 | src: "https://images.pexels.com/photos/12204293/pexels-photo-12204293.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1",
26 | title: "Vinyl Loveless Happiness",
27 | category: "Vinyl Record",
28 | },
29 | ];
30 |
31 | const Gallery = () => {
32 | return (
33 |
34 |
35 |
36 | {images.map((image) => (
37 |
38 | ))}
39 |
40 |
41 | );
42 | };
43 |
44 | export default Gallery;
45 |
--------------------------------------------------------------------------------
/src/components/NotFound.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import { useGsapNotFoundHeadline, useGsapNotFoundImg } from "../hooks/gsap";
3 |
4 | const NotFound = () => {
5 | const leftHeadlineRef = useRef(null);
6 | const rightHeadlineRef = useRef(null);
7 | const leftImgRef = useRef(null);
8 | const rightImgRef = useRef(null);
9 |
10 | useGsapNotFoundHeadline(leftHeadlineRef);
11 | useGsapNotFoundHeadline(rightHeadlineRef, "100vw");
12 | useGsapNotFoundImg(leftImgRef);
13 | useGsapNotFoundImg(rightImgRef);
14 |
15 | return (
16 |
17 |
18 | Sorry, we couldn't
19 |
20 |
21 |
25 |
26 |
27 |
31 |
32 |
33 | Find that page
34 |
35 |
36 | );
37 | };
38 |
39 | export default NotFound;
40 |
--------------------------------------------------------------------------------
/src/components/Featured.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | useGsapFeaturedLeftShutter,
4 | useGsapFeaturedRightShutter,
5 | } from "../hooks/gsap";
6 | import SectionTitle from "./SectionTitle";
7 |
8 | const Featured = ({ minHeight = false }) => {
9 | const featuredRef = useRef(null);
10 | const featuredLeftShutter = useRef(null);
11 | const featuredRightShutter = useRef(null);
12 |
13 | useGsapFeaturedLeftShutter(featuredLeftShutter, featuredRef);
14 | useGsapFeaturedRightShutter(featuredRightShutter, featuredRef);
15 |
16 | return (
17 |
22 |
23 |
24 |
25 |
90's Telephone
26 |
30 |
31 |
32 |
33 |
90's Cassette Player
34 |
38 |
39 |
40 |
41 |
42 | );
43 | };
44 |
45 | export default Featured;
46 |
--------------------------------------------------------------------------------
/src/data/blogs.js:
--------------------------------------------------------------------------------
1 | export const blogsArr = [
2 | {
3 | id: 1,
4 | title: "Spice Girls",
5 | img: "https://imissthe90s.files.wordpress.com/2010/04/spice-girls.jpg?w=500&h=375",
6 | author: "imissthe90s",
7 | date: "April 22, 2010",
8 | body: "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.",
9 | },
10 | {
11 | id: 2,
12 | title: "Sega Mega Drive",
13 | img: "https://imissthe90s.files.wordpress.com/2010/04/sega1.jpg",
14 | author: "imissthe90s",
15 | date: "April 15, 2010",
16 | body: "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.",
17 | },
18 | {
19 | id: 3,
20 | title: "Virgin Cola",
21 | img: "https://imissthe90s.files.wordpress.com/2010/04/virgin-cola.png?w=500&h=384",
22 | author: "imissthe90s",
23 | date: "April 6, 2010",
24 | body: "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.",
25 | },
26 | {
27 | id: 4,
28 | title: "Yo-yos",
29 | img: "https://imissthe90s.files.wordpress.com/2010/03/yoyo.jpg?w=500&h=339",
30 | author: "imissthe90s",
31 | date: "March 22, 2010",
32 | body: "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.",
33 | },
34 | ];
35 |
--------------------------------------------------------------------------------
/src/components/Hero.js:
--------------------------------------------------------------------------------
1 | import { useRef } from "react";
2 | import {
3 | useGsapShutterUnveil,
4 | useGsapPhotoScroller,
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 | const photo1Ref = useRef(null);
13 | const photo2Ref = useRef(null);
14 | const photo3Ref = useRef(null);
15 | const photo4Ref = useRef(null);
16 | const photo5Ref = useRef(null);
17 |
18 | const photosArr = [photo1Ref, photo2Ref, photo3Ref, photo4Ref, photo5Ref];
19 |
20 | useGsapShutterUnveil(shutter1, 0, heroRef);
21 | useGsapShutterUnveil(shutter2, 0.3, heroRef);
22 | useGsapPhotoScroller(photosArr);
23 | useGsapPhotoLevitate(photosArr, heroRef);
24 |
25 | return (
26 |
27 |
28 | Ethereal
29 |
30 |
31 | Canvas
32 |
33 |
34 |
42 |
50 |
58 |
66 |
74 |
75 |
76 | );
77 | };
78 |
79 | export default Hero;
80 |
--------------------------------------------------------------------------------
/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 |
7 | export const useGsapDownStagger = (arr, delay = 0) => {
8 | useEffect(() => {
9 | const el = arr.map((item) => item.current);
10 |
11 | gsap.fromTo(
12 | el,
13 | {
14 | y: "-100%",
15 | opacity: 0,
16 | },
17 | {
18 | y: 0,
19 | opacity: 1,
20 | duration: 1.2,
21 | stagger: 0.1,
22 | delay: delay,
23 | ease: Expo.easeInOut,
24 | }
25 | );
26 | }, []);
27 | };
28 |
29 | export const useGsapUpward = (item, delay = 0) => {
30 | useEffect(() => {
31 | const el = item.current;
32 |
33 | gsap.fromTo(
34 | el,
35 | {
36 | y: "100%",
37 | opacity: 0,
38 | },
39 | {
40 | y: 0,
41 | opacity: 1,
42 | duration: 1,
43 | delay: delay,
44 | ease: Expo.easeInOut,
45 | }
46 | );
47 | }, []);
48 | };
49 |
50 | export const useGsapShutterUnveil = (item, delay = 0, trig) => {
51 | useEffect(() => {
52 | const el = item.current;
53 |
54 | gsap.fromTo(
55 | el,
56 | {
57 | height: "100%",
58 | },
59 | {
60 | height: 0,
61 | duration: 2,
62 | delay: delay,
63 | ease: Expo.easeInOut,
64 | scrollTrigger: {
65 | trigger: trig.current,
66 | toggleActions: "play reverse play reverse",
67 | },
68 | }
69 | );
70 | }, []);
71 | };
72 |
73 | export const useGsapLeftWalking = (item) => {
74 | useEffect(() => {
75 | const el = item.current;
76 |
77 | gsap.fromTo(
78 | el,
79 | {
80 | x: "-100vw",
81 | },
82 | {
83 | x: 0,
84 | duration: 1.5,
85 | ease: Expo.easeInOut,
86 | scrollTrigger: {
87 | trigger: el,
88 | toggleActions: "play",
89 | },
90 | }
91 | );
92 | }, []);
93 | };
94 |
95 | export const useGsapFeaturedLeftShutter = (item, trig) => {
96 | useEffect(() => {
97 | const el = item.current;
98 |
99 | gsap.fromTo(
100 | el,
101 | {
102 | height: "100%",
103 | },
104 | {
105 | height: 0,
106 | duration: 1.2,
107 | ease: Expo.easeInOut,
108 | scrollTrigger: {
109 | trigger: trig.current,
110 | start: "top center",
111 | end: "bottom center",
112 | toggleActions: "play reverse play reverse",
113 | },
114 | }
115 | );
116 | }, []);
117 | };
118 |
119 | export const useGsapFeaturedRightShutter = (item, trig) => {
120 | useEffect(() => {
121 | const el = item.current;
122 |
123 | gsap.fromTo(
124 | el,
125 | {
126 | width: "100%",
127 | },
128 | {
129 | width: 0,
130 | duration: 1.2,
131 | delay: 0.2,
132 | ease: Expo.easeInOut,
133 | scrollTrigger: {
134 | trigger: trig.current,
135 | start: "top center",
136 | end: "bottom center",
137 | toggleActions: "play reverse play reverse",
138 | },
139 | }
140 | );
141 | }, []);
142 | };
143 |
144 | export const useGsapGalleryImg = (item) => {
145 | useEffect(() => {
146 | const el = item.current;
147 |
148 | gsap.fromTo(
149 | el,
150 | {
151 | width: 0,
152 | x: 0,
153 | },
154 | {
155 | width: "100%",
156 | x: "30%",
157 | duration: 1,
158 | ease: Expo.easeInOut,
159 | scrollTrigger: {
160 | trigger: el,
161 | start: "top center",
162 | end: "bottom top",
163 | toggleActions: "play reverse play reverse",
164 | },
165 | }
166 | );
167 | }, []);
168 | };
169 |
170 | export const useGsapGalleryTitle = (item, trig) => {
171 | useEffect(() => {
172 | const el = item.current;
173 |
174 | gsap.fromTo(
175 | el,
176 | {
177 | x: "30%",
178 | },
179 | {
180 | x: 0,
181 | duration: 1,
182 | ease: Expo.easeInOut,
183 | scrollTrigger: {
184 | trigger: trig.current,
185 | start: "top center",
186 | end: "bottom top",
187 | toggleActions: "play reverse play reverse",
188 | },
189 | }
190 | );
191 | }, []);
192 | };
193 |
194 | export const useGsapGalleryCategory = (item, trig) => {
195 | useEffect(() => {
196 | const el = item.current;
197 |
198 | gsap.fromTo(
199 | el,
200 | {
201 | x: "-100vw",
202 | },
203 | {
204 | x: 0,
205 | duration: 1,
206 | ease: Expo.easeInOut,
207 | scrollTrigger: {
208 | trigger: trig.current,
209 | start: "top center",
210 | end: "bottom top",
211 | toggleActions: "play reverse play reverse",
212 | },
213 | }
214 | );
215 | }, []);
216 | };
217 |
218 | export const useGsapFooterHeadline = (item, trig) => {
219 | useEffect(() => {
220 | const el = item.current;
221 |
222 | gsap.fromTo(
223 | el,
224 | {
225 | y: "-100%",
226 | },
227 | {
228 | y: 0,
229 | duration: 1,
230 | ease: Expo.easeInOut,
231 | scrollTrigger: {
232 | trigger: trig.current,
233 | toggleActions: "play",
234 | },
235 | }
236 | );
237 | }, []);
238 | };
239 |
240 | export const useGsapNotFoundHeadline = (item, vw = "-100vw") => {
241 | useEffect(() => {
242 | const el = item.current;
243 |
244 | gsap.fromTo(
245 | el,
246 | {
247 | x: vw,
248 | },
249 | {
250 | x: 0,
251 | duration: 1.5,
252 | ease: Expo.easeInOut,
253 | }
254 | );
255 | }, []);
256 | };
257 |
258 | export const useGsapNotFoundImg = (item) => {
259 | useEffect(() => {
260 | const el = item.current;
261 |
262 | gsap.fromTo(
263 | el,
264 | {
265 | scale: 0,
266 | borderRadius: "50%",
267 | },
268 | {
269 | scale: 1,
270 | borderRadius: 0,
271 | duration: 4,
272 | delay: 1,
273 | ease: "elastic",
274 | }
275 | );
276 | }, []);
277 | };
278 |
279 | export const useGsapPhotoScroller = (arr) => {
280 | useEffect(() => {
281 | const el = arr.map((item) => item.current);
282 |
283 | gsap.fromTo(
284 | el,
285 | {
286 | y: "-100vh",
287 | scale: 0,
288 | },
289 | {
290 | y: 0,
291 | scale: 1,
292 | delay: 2.2,
293 | duration: 2,
294 | stagger: 0.2,
295 | ease: Expo.easeInOut,
296 | }
297 | );
298 | }, []);
299 | };
300 |
301 | export const useGsapPhotoLevitate = (arr, trig) => {
302 | useEffect(() => {
303 | const el = arr.map((item) => item.current);
304 |
305 | gsap.fromTo(
306 | el,
307 | {
308 | y: 0,
309 | },
310 | {
311 | y: "-30%",
312 | ease: Expo.easeInOut,
313 | scrollTrigger: {
314 | trigger: trig.current,
315 | scrub: 1,
316 | toggleActions: "play reverse play reverse",
317 | },
318 | }
319 | );
320 | }, []);
321 | };
322 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap");
2 | @import url("https://fonts.googleapis.com/css2?family=Bai+Jamjuree:wght@200;300;400;500;600;700&display=swap");
3 | @import url("https://fonts.googleapis.com/css2?family=Syncopate:wght@400;700&display=swap");
4 | @import url("https://fonts.googleapis.com/css2?family=Bodoni+Moda:wght@400;500;600;700;800;900&display=swap");
5 |
6 | :root {
7 | --color-primary: #d53f41;
8 | --color-dark: #626262;
9 | --color-darker: #464646;
10 | --color-light: #dbd8d6;
11 | --color-lighter: #f5f0ec;
12 | }
13 |
14 | * {
15 | margin: 0;
16 | padding: 0;
17 | list-style: none;
18 | text-decoration: none;
19 | box-sizing: border-box;
20 | }
21 |
22 | html,
23 | body {
24 | overflow-x: hidden;
25 | }
26 |
27 | html {
28 | font-size: 62.5%;
29 | }
30 |
31 | body {
32 | font-family: "Poppins", sans-serif;
33 | font-size: 2rem;
34 | font-weight: 400;
35 | line-height: 1.7;
36 | letter-spacing: 1px;
37 | background-color: var(--color-lighter);
38 | color: var(--color-darker);
39 | }
40 |
41 | .wrapper {
42 | margin: 0 5vw;
43 | }
44 |
45 | section {
46 | padding: 10vw 0;
47 | }
48 |
49 | .min-h-100vh {
50 | min-height: 100vh;
51 | }
52 |
53 | .section-title {
54 | font-family: "Syncopate", sans-serif;
55 | font-size: 1.5rem;
56 | text-transform: lowercase;
57 | padding-bottom: 5vw;
58 | color: var(--color-primary);
59 | }
60 |
61 | .navbar {
62 | display: flex;
63 | justify-content: space-between;
64 | align-items: flex-start;
65 | padding: 2rem 0;
66 | font-family: "Syncopate", sans-serif;
67 | font-size: 1.5rem;
68 | text-transform: lowercase;
69 | }
70 |
71 | .links,
72 | .favourite-link {
73 | font-weight: 600;
74 | }
75 |
76 | .links a,
77 | .favourite-link a {
78 | color: var(--color-dark);
79 | position: relative;
80 | }
81 |
82 | .links a::before,
83 | .favourite-link a::before {
84 | content: "";
85 | width: 0%;
86 | height: 0.2rem;
87 | position: absolute;
88 | top: 50%;
89 | left: 50%;
90 | transform: translate(-50%, -50%);
91 | background-color: var(--color-primary);
92 | transition: 0.5s;
93 | }
94 |
95 | .links a:hover::before,
96 | .favourite-link a:hover::before {
97 | width: 120%;
98 | }
99 |
100 | .logo a {
101 | color: inherit;
102 | }
103 |
104 | .hero {
105 | font-family: "Bai Jamjuree", sans-serif;
106 | text-transform: uppercase;
107 | font-size: 10vw;
108 | text-align: center;
109 | line-height: 1.2;
110 | color: var(--color-darker);
111 | min-height: 100vh;
112 | display: flex;
113 | flex-direction: column;
114 | justify-content: center;
115 | align-items: center;
116 | position: relative;
117 | }
118 |
119 | .ethereal,
120 | .canvas {
121 | position: relative;
122 | }
123 |
124 | .ethereal span,
125 | .canvas span {
126 | position: absolute;
127 | top: 0;
128 | left: 0;
129 | right: 0;
130 | bottom: 0;
131 | z-index: 1;
132 | width: 100%;
133 | height: 100%;
134 | background-color: var(--color-lighter);
135 | }
136 |
137 | .photos {
138 | position: absolute;
139 | top: 0;
140 | left: 0;
141 | bottom: 0;
142 | right: 0;
143 | z-index: 1;
144 | display: grid;
145 | grid-template-columns: repeat(7, 1fr);
146 | grid-template-rows: repeat(5, 1fr);
147 | }
148 |
149 | .photo {
150 | width: 100%;
151 | height: 100%;
152 | overflow: hidden;
153 | background-position: center;
154 | background-repeat: no-repeat;
155 | background-size: cover;
156 | }
157 |
158 | .photo.one {
159 | grid-column: 1;
160 | grid-row: 2;
161 | }
162 |
163 | .photo.two {
164 | grid-column: 4;
165 | grid-row: 3;
166 | }
167 |
168 | .photo.three {
169 | grid-column: 2;
170 | grid-row: 5;
171 | }
172 |
173 | .photo.four {
174 | grid-column: 7;
175 | grid-row: 4;
176 | }
177 |
178 | .photo.five {
179 | grid-column: 5;
180 | grid-row: 1;
181 | }
182 |
183 | .featured-wrapper {
184 | display: grid;
185 | grid-template-columns: 30% auto;
186 | align-items: center;
187 | gap: 10rem;
188 | }
189 |
190 | .featured-wrapper span {
191 | text-transform: uppercase;
192 | font-weight: 500;
193 | letter-spacing: 5px;
194 | }
195 |
196 | .featured-wrapper img {
197 | width: 100%;
198 | display: block;
199 | }
200 |
201 | .featured-left {
202 | display: grid;
203 | gap: 1rem;
204 | position: relative;
205 | }
206 |
207 | .left-shutter {
208 | position: absolute;
209 | z-index: 1;
210 | width: 100%;
211 | height: 100%;
212 | top: 0;
213 | left: 0;
214 | right: 0;
215 | bottom: 0;
216 | background-color: var(--color-lighter);
217 | }
218 |
219 | .featured-right {
220 | display: grid;
221 | gap: 1rem;
222 | position: relative;
223 | }
224 |
225 | .right-shutter {
226 | position: absolute;
227 | z-index: 1;
228 | width: 100%;
229 | height: 100%;
230 | top: 0;
231 | left: 0;
232 | right: 0;
233 | bottom: 0;
234 | background-color: var(--color-lighter);
235 | }
236 |
237 | .about p {
238 | font-size: 3vw;
239 | line-height: 1.5;
240 | }
241 |
242 | .about p:last-child {
243 | margin-top: 3vw;
244 | }
245 |
246 | .gallery-wrapper {
247 | display: grid;
248 | grid-template-columns: 1fr;
249 | justify-items: center;
250 | gap: 10vw;
251 | padding: 10vw;
252 | background-color: var(--color-primary);
253 | }
254 |
255 | .gallery-item {
256 | position: relative;
257 | width: 50%;
258 | }
259 |
260 | .gallery-item-title {
261 | position: absolute;
262 | top: 10%;
263 | left: -50%;
264 | font-family: "Bai Jamjuree", sans-serif;
265 | text-transform: uppercase;
266 | color: var(--color-lighter);
267 | font-size: 8vw;
268 | line-height: 1.2;
269 | mix-blend-mode: color-dodge;
270 | z-index: 1;
271 | }
272 |
273 | .gallery-item-category {
274 | position: absolute;
275 | bottom: -5%;
276 | left: 0;
277 | text-transform: uppercase;
278 | color: var(--color-lighter);
279 | letter-spacing: 10px;
280 | z-index: 1;
281 | }
282 |
283 | .gallery-item-image {
284 | background-position: center;
285 | background-repeat: no-repeat;
286 | background-size: cover;
287 | width: 100%;
288 | height: 100vh;
289 | }
290 |
291 | .not-found {
292 | min-height: 100vh;
293 | display: grid;
294 | grid-template-columns: 1fr 1fr;
295 | align-content: flex-start;
296 | column-gap: 5vw;
297 | row-gap: 1vw;
298 | padding: 5vw 0;
299 | }
300 |
301 | .headline-1,
302 | .headline-2 {
303 | font-family: "Bai Jamjuree", sans-serif;
304 | font-size: 8vw;
305 | font-weight: 700;
306 | line-height: 1;
307 | text-transform: capitalize;
308 | }
309 |
310 | .img-1,
311 | .img-2 {
312 | width: 20vw;
313 | height: 20vw;
314 | overflow: hidden;
315 | display: flex;
316 | justify-content: center;
317 | align-items: center;
318 | z-index: 1;
319 | }
320 |
321 | .img-1 img,
322 | .img-2 img {
323 | display: block;
324 | width: 100%;
325 | }
326 |
327 | .img-2 {
328 | justify-self: self-end;
329 | }
330 |
331 | .blogs {
332 | display: flex;
333 | flex-direction: column;
334 | gap: 5rem;
335 | }
336 |
337 | .blog {
338 | display: grid;
339 | grid-template-columns: repeat(4, 1fr);
340 | gap: 2rem;
341 | }
342 |
343 | .blog-img {
344 | width: 40rem;
345 | height: 40rem;
346 | overflow: hidden;
347 | display: flex;
348 | justify-content: center;
349 | align-items: flex-start;
350 | border-radius: 3px;
351 | }
352 |
353 | .blog-img img {
354 | width: 100%;
355 | display: block;
356 | }
357 |
358 | .blog-texts {
359 | display: flex;
360 | flex-direction: column;
361 | gap: 1.2rem;
362 | align-items: flex-start;
363 | grid-column: 2/-1;
364 | }
365 |
366 | .blog-publisher {
367 | font-size: 1.5rem;
368 | }
369 |
370 | .blog-publisher .author,
371 | .blog-publisher .date {
372 | color: var(--color-primary);
373 | }
374 |
375 | .blog-btn {
376 | font-family: inherit;
377 | font-size: inherit;
378 | border: none;
379 | background-color: var(--color-primary);
380 | color: var(--color-lighter);
381 | padding: 1rem 2rem;
382 | border-radius: 3px;
383 | cursor: pointer;
384 | transition: 0.5s;
385 | margin-top: 0.2rem;
386 | }
387 |
388 | .blog-btn:hover {
389 | background-color: var(--color-darker);
390 | }
391 |
392 | .footer {
393 | text-align: center;
394 | }
395 |
396 | .footer h1 {
397 | font-family: "Bodoni Moda", serif;
398 | font-size: 10vw;
399 | text-transform: lowercase;
400 | color: var(--color-primary);
401 | z-index: -1;
402 | }
403 |
404 | @media screen and (max-width: 768px) {
405 | html {
406 | font-size: 50%;
407 | }
408 |
409 | .wrapper {
410 | margin: 0 2.5vw;
411 | }
412 |
413 | section {
414 | padding: 5vw 0;
415 | }
416 |
417 | .section-title {
418 | padding-bottom: 2.5vw;
419 | }
420 |
421 | .navbar {
422 | padding: 1rem 0;
423 | flex-direction: column;
424 | }
425 |
426 | .logo {
427 | order: -1;
428 | }
429 |
430 | .featured-wrapper {
431 | grid-template-columns: 1fr;
432 | gap: 5rem;
433 | }
434 |
435 | .blog {
436 | grid-template-columns: 1fr;
437 | }
438 |
439 | .blog-texts {
440 | grid-column: auto;
441 | }
442 | }
443 |
--------------------------------------------------------------------------------