Tutorials

avatar

Malik Kotb

Feb 19, 2024 / Beginner / Short

Color Scroll
Color Scroll Transition with Next.js and TailwindCSS

A smooth color transition on scroll using JavaScript and TailwindCSS. Inspired by: https://backstagetalks.com/

Initializing the project

Let's start the project by setting up a Next.js app with TypeScript and utilizing the app router. Just run npx create-next-app@latest in the terminal.

We can clear out all the contents in the page.tsx, and global.css files, and add our own HTML and CSS, to begin with a clean slate in the application.

Item Component

Create a components directory (next to the app directory) and create a new file Item.tsx.

page.tsx

1"use client";
2import Image from "next/image";
3export default function Item({ source, index, isLast })  {
4	return (
5		<div className={`h-[${isLast ? 100 : 125}vh] items-center justify-center flex flex-col gap-2 m-0 p-0`}>
6			<div className="mt-[-10%]">
7				<Image src={source} alt={source} width={400} height={500} className="shadow-xl" />
8				<div className="flex flex-col p-4 mt-4 gap-2 text-center">
9					<p className="text-xl font-medium">Issue #{index + 1}</p>
10					<p className="text-lg font-medium text-white">GET HERE (Europe)</p>
11					<p className="text-sm font-medium">
12					or visit at <span className="text-white">selected museums</span>
13					</p>
14				</div>
15			</div>
16		</div>
17	);
18}
19

Page Component

Where the magic happens..

You can define a list of image-paths and list of correspondong colors outside the JSX component. We will get the current scroll position and viewport height and then determine which section the user is currently in

page.tsx

1import { useEffect, useState }  from  "react";
2import Item from  "./components/Item";
3export  default  function  Home()  {
4	const  [backgroundColor,  setBackgroundColor]  =  useState("#00c1b5");
5	
6	useEffect(() => {
7		const changeBackgroundOnScroll = () => {
8			const scrollPosition = window.scrollY;
9			const viewportHeight = window.innerHeight;
10			const index = Math.floor(scrollPosition  /  viewportHeight);
11			setBackgroundColor(colors[index >= 4 ? 4 : index]);
12		};
13		window.addEventListener("scroll", changeBackgroundOnScroll);
14		return () => { window.removeEventListener("scroll", changeBackgroundOnScroll );
15		};
16	}, []);
17	
18	return (
19		<section
20			style={{ backgroundColor: backgroundColor,
21			transition: "background-color 0.5s ease-out"}}>
22				{images.map((image, index) => (
23					<Item key={index} source={image}
24						 index={index} 
25						 isLast={index === images.length - 1} />
26			         ))}
27		</section>
28);
29}
30
We should have something like this:
Wrapping up.

Quick and smooth, right?

Hope you liked the animation and learned something new!

-Malik

Things for creative devs
sent to your inbox every week