Paragraphe keynote
Un très grand paragraphe s’allume mot à mot au défilement, comme une keynote. Entourez des mots d’*astérisques* pour les colorer.
Réglages
Code
import { Fragment, useRef } from "react";
import { motion, useScroll, useSpring, useTransform, type MotionValue } from "framer-motion";
interface KeynoteScrollTextProps {
/** Rodea con asteriscos las palabras que quieras destacar: *así*. */
text?: string;
accent?: string;
/** Opacidad (0-1) de las palabras que aún no se han leído. */
dim?: number;
/** true: caja con scroll propio. false: usa el scroll de la página. */
contained?: boolean;
}
function Word({
word,
progress,
range,
accent,
dim,
}: {
word: string;
progress: MotionValue<number>;
range: [number, number];
accent: string;
dim: number;
}) {
const opacity = useTransform(progress, range, [dim, 1]);
return (
<motion.span style={{ opacity, color: word.includes("*") ? accent : undefined }}>
{word.replace(/\*/g, "")}
</motion.span>
);
}
export default function KeynoteScrollText({
text = "Nous concevons chaque détail pour qu’il disparaisse. Il reste la *sensation* que tout *fonctionne* *simplement.*",
accent = "#2997ff",
dim = 0.14,
contained = true,
}: KeynoteScrollTextProps) {
const scroller = useRef<HTMLDivElement>(null);
const target = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll(
contained
? { container: scroller, target, offset: ["start start", "end end"] }
: { target, offset: ["start start", "end end"] },
);
const progress = useSpring(scrollYProgress, { stiffness: 140, damping: 30, restDelta: 0.0005 });
const words = text.split(/\s+/).filter(Boolean);
const lead = 0.28;
const step = (0.94 + lead) / (words.length + 2);
const section = (
<div ref={target} className={contained ? "relative h-[320%]" : "relative h-[320vh]"}>
<div
className={
(contained ? "h-[31.25%]" : "h-screen") +
" sticky top-0 flex items-center px-[8%] [container-type:inline-size]"
}
>
<p className="text-[clamp(28px,10cqi,76px)] font-semibold leading-[1.08] tracking-[-0.035em]">
<span className="sr-only">{text.replace(/\*/g, "")}</span>
<span aria-hidden="true">
{words.map((word, i) => (
<Fragment key={word + "-" + i}>
<Word
word={word}
progress={progress}
range={[-lead + i * step, -lead + (i + 3) * step]}
accent={accent}
dim={dim}
/>{" "}
</Fragment>
))}
</span>
</p>
</div>
</div>
);
const theme = "bg-white text-[#1d1d1f] dark:bg-black dark:text-[#f5f5f7]";
if (!contained) return <div className={theme}>{section}</div>;
return (
<div
ref={scroller}
className={"relative h-[32rem] w-full overflow-y-auto rounded-3xl [scrollbar-width:none] " + theme}
>
{section}
</div>
);
}
Prompt pour l’IA
Collez-le dans ChatGPT, Claude ou Cursor : le bloc sera adapté à votre projet avec ces réglages, même sans React.
Plus dans Texte et défilement
Voir tout : Texte et défilementOù l’utiliser
Cet effet de texte qui s’éclaire au scroll, vu sur les pages produit d’Apple, marche le mieux avec une seule phrase forte : le manifeste d’une agence, le « pourquoi nous existons » d’une startup, la première ligne d’une page à propos. Les mots passent du pâle au plein au rythme du lecteur, et la phrase porte davantage.
Tenez-vous-en à une ou deux phrases ; un paragraphe entier devient pénible à faire défiler. Ne colorez que deux ou trois mots. Les lecteurs d’écran reçoivent tout le texte d’un coup, et le premier réglage à toucher est l’opacité des mots pas encore lus.