Back to library
INTERACTION_074

Sentence Opacity Progression.

Sentences brighten in turn so attention moves down the block. Every sentence at 30% opacity before the trigger; raising each sentence to full opacity as it reaches the reading line once scroll progress through the block.

CATEGORY
Copy
COMPLEXITY
Intermediate
TECH
CSS
PLATFORM
Both
PERFORMANCE
Light
LIVE PLAYGROUND
CSS / CSS
/* Sentence Opacity Progression — Sentences brighten in turn so attention moves down the block. */
.sentence-opacity-progression {
  --dur: scroll-linked;
  --ease: linear;
  --stagger: 60ms;
  will-change: transform, opacity;
}

.sentence-opacity-progression > * {
  opacity: 0;
  transform: translateY(14px);
  animation: sentence-opacity-progression-in var(--dur) var(--ease) forwards;
  animation-delay: calc(var(--i, 0) * var(--stagger));
}

/* every sentence at 30% opacity -> raising each sentence to full opacity as it reaches the reading line */
@keyframes sentence-opacity-progression-in {
  to {
    opacity: 1;
    transform: none;
    filter: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sentence-opacity-progression > * {
    opacity: 1;
    transform: none;
    animation: none;
  }
}
React / TSX
import { useEffect, useRef } from "react";

/** Sentence Opacity Progression — trigger: scroll progress through the block */
export function SentenceOpacityProgression({ children }: { children: React.ReactNode }) {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
      el.dataset["state"] = "in";
      return;
    }
    const io = new IntersectionObserver(
      ([entry]) => {
        if (entry?.isIntersecting) {
          el.dataset["state"] = "in";
          io.unobserve(el);
        }
      },
      { threshold: 0.2 },
    );
    io.observe(el);
    return () => io.disconnect();
  }, []);

  return (
    <div ref={ref} className="sentence-opacity-progression" data-state="out">
      {children}
    </div>
  );
}

IF YOU LIKE THIS, STEAL THESE TOO.

INTERACTION_077CSS

Paragraph Masking

A paragraph is revealed behind a soft gradient mask as it enters.

Copy / LightView
+
INTERACTION_079CSS

Expandable Supporting Copy

Extra detail expands smoothly without a jump in layout.

Copy / LightView
INTERACTION_075CSS

Reading Progress Highlight

A thin progress rule tracks how far the reader has moved.

Copy / LightView
INTERACTION_076CSS + JS

Scroll Word Activation

Individual words light up as the scroll line passes through them.

Copy / LightView