Text Gradient Scroll
Text Gradient Scroll is a React and Tailwind CSS text component from Bund UI, licensed MIT. Copy it and paste it into your project.
Bund UI
MIT
text
What it is
Text Gradient Scroll is a React and Tailwind CSS text component from Bund UI, licensed MIT. Copy it and paste it into your project.
How to use it
Open it in the catalogue, press copy, and paste it into your project. The prompt you copy carries the code and the rules that tell your agent to reproduce it without changing anything.
Five free copies a month. No card.
Needs
npm i lenis motion lucide-react
Licence
This component comes from Bund UI and is redistributed under the MIT licence, which permits commercial use. The copyright notice travels with the code.
Text Gradient Scroll
"use client";
import TextGradientScroll from "./text-gradient-scroll";
import { type LenisRef, ReactLenis } from "lenis/react";
import { useEffect, useRef } from "react";
import { cancelFrame, frame } from "motion/react";
import { ChevronDown, ChevronUp } from "lucide-react";
export default function TextGradientScrollExample() {
const lenisRef = useRef<LenisRef>(null);
useEffect(() => {
function update(data: { timestamp: number }) {
const time = data.timestamp;
lenisRef.current?.lenis?.raf(time);
}
frame.update(update, true);
return () => cancelFrame(update);
}, []);
return (
<>
<ReactLenis root options={{ autoRaf: false }} ref={lenisRef} />
<div>
<div className="flex h-screen items-center justify-center">
Scroll Down <ChevronDown className="text-muted-foreground ms-1 size-4" />
</div>
<div className="flex flex-col gap-10">
<TextGradientScroll
className="text-xl"
text="The text gradient scroll component is designed to enhance user interaction by providing a visually dynamic effect as the user scrolls through the text. Unlike static text, this effect offers a more engaging visual experience with smooth color transitions that change as the text is scrolled. The text gradient scroll component is designed to enhance user interaction by providing a visually dynamic effect as the user scrolls through the text. Unlike static text, this effect offers a more engaging visual experience with smooth color transitions that change as the text is scrolled."
/>
<TextGradientScroll
className="text-xl text-green-500"
text="The text gradient scroll component is designed to enhance user interaction by providing a visually dynamic effect as the user scrolls through the text. Unlike static text, this effect offers a more engaging visual experience with smooth color transitions that change as the text is scrolled."
/>
</div>
<div className="flex h-screen items-center justify-center">
Scroll Up <ChevronUp className="text-muted-foreground ms-1 size-4" />
</div>
</div>
</>
);
}
"use client";
import React, { createContext, useContext, useRef } from "react";
import { motion, MotionValue, useScroll, useTransform } from "motion/react";
import { cn } from "@/lib/utils";
type TextOpacityEnum = "none" | "soft" | "medium";
type ViewTypeEnum = "word" | "letter";
type TextGradientScrollType = {
text: string;
type?: ViewTypeEnum;
className?: string;
textOpacity?: TextOpacityEnum;
};
type LetterType = {
children: React.ReactNode | string;
progress: MotionValue<number>;
range: number[];
};
type WordType = {
children: React.ReactNode;
progress: MotionValue<number>;
range: number[];
};
type CharType = {
children: React.ReactNode;
progress: MotionValue<number>;
range: number[];
};
type TextGradientScrollContextType = {
textOpacity?: TextOpacityEnum;
type?: ViewTypeEnum;
};
const TextGradientScrollContext = createContext<TextGradientScrollContextType>({});
function useGradientScroll() {
return useContext(TextGradientScrollContext);
}
export default function TextGradientScroll({
text,
className,
type = "letter",
textOpacity = "soft"
}: TextGradientScrollType) {
const ref = useRef<HTMLParagraphElement>(null);
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start center", "end center"]
});
const words = text.split(" ");
return (
<TextGradientScrollContext.Provider value={{ textOpacity, type }}>
<p ref={ref} className={cn("relative m-0 flex flex-wrap", className)}>
{words.map((word, i) => {
const start = i / words.length;
const end = start + 1 / words.length;
return type === "word" ? (
<Word key={i} progress={scrollYProgress} range={[start, end]}>
{word}
</Word>
) : (
<Letter key={i} progress={scrollYProgress} range={[start, end]}>
{word}
</Letter>
);
})}
</p>
</TextGradientScrollContext.Provider>
);
}
const Word = ({ children, progress, range }: WordType) => {
const opacity = useTransform(progress, range, [0, 1]);
return (
<span className="relative me-2 mt-2">
<span style={{ position: "absolute", opacity: 0.1 }}>{children}</span>
<motion.span style={{ transition: "all .5s", opacity: opacity }}>{children}</motion.span>
</span>
);
};
const Letter = ({ children, progress, range }: LetterType) => {
if (typeof children === "string") {
const amount = range[1] - range[0];
const step = amount / children.length;
return (
<span className="relative me-2 mt-2">
{children.split("").map((char: string, i: number) => {
const start = range[0] + i * step;
const end = range[0] + (i + 1) * step;
return (
<Char key={`c_${i}`} progress={progress} range={[start, end]}>
{char}
</Char>
);
})}
</span>
);
}
};
const Char = ({ children, progress, range }: CharType) => {
const opacity = useTransform(progress, range, [0, 1]);
const { textOpacity } = useGradientScroll();
return (
<span>
<span
className={cn("absolute", {
"opacity-0": textOpacity == "none",
"opacity-10": textOpacity == "soft",
"opacity-30": textOpacity == "medium"
})}>
{children}
</span>
<motion.span
style={{
transition: "all .5s",
opacity: opacity
}}>
{children}
</motion.span>
</span>
);
};